Skip to content

Commit 6707052

Browse files
committed
Fix audited 0.25 correctness and security gaps with CKB-VM evidence
Close the swarm-audit findings on the Move-informed 0.25 surface: stop dynamic Molecule-table field validation from clobbering live u128 limbs by routing wide binary operands through shared stack-spilled loading, keep constant folding from wrapping arithmetic the runtime traps on, decide match exhaustiveness with a bounded constructor matrix that merges nested and or-pattern payload coverage, derive Cell-backed value abilities structurally through struct/enum fields, and register E2501 with the shared error registry. Also enforce real package boundaries for public(package) visibility, specialize generic templates in their owning module, bind typed semantics v2 and lowering v3 to machine records checked by the standalone checker, and tighten Registry SemVer admission. Add the missing exact CKB-VM execution vectors for u128 bitwise, addition, and shift over dynamic-schema fields (verified to fail on the pre-fix code generator), plus the audit_poc reproduction corpus. 💘 Generated with Crush Assisted-by: Crush:glm-5.2
1 parent 8c0a14b commit 6707052

48 files changed

Lines changed: 4252 additions & 369 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎BRANCHES.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Branch Context
22

3+
## nightly-0.25
4+
5+
`nightly-0.25` is the active language-completeness development line. It adds
6+
bounded value generics, explicit visibility and package interfaces, exhaustive
7+
IR-surface classification, and the typed-semantics v2 / lowering-record v3
8+
boundary. Treat it as merge-ready only when compiler, independent checker,
9+
Registry, editor/Playground, docs, and the `dev`, `ci`, and `backend` gates all
10+
agree. It is not a stable release or production CKB evidence claim; the crate
11+
version remains pinned until the coordinated release gate changes it.
12+
313
## 0.12-era proposal baseline
414

515
The 0.12-era work is the formal proposal baseline for grant-style acceptance

‎CHANGELOG.md‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,21 @@
1818
reborrowing while preserving non-escape and lifecycle-crossing rejection.
1919
Add explicit public/package/private visibility, canonical package interfaces,
2020
six-dimensional interface compatibility with stable E2501 rejection,
21-
Registry interface/hash admission, metadata schema 60 typed-semantics records,
22-
lowering-record v2 and independent V2419/V2420 typed-to-machine checks. Add
21+
Registry interface/hash admission, metadata schema 61 typed-semantics-v2 records,
22+
lowering-record v3 and independent V2419/V2420 typed-to-machine checks. Generic
23+
templates now specialize across package boundaries in their owning module;
24+
implementation monomorphizations no longer pollute package interfaces. Add
2325
labeled break/continue CFG lowering and CKB-VM evidence. Upgrade the VS Code
2426
grammar and Playground highlighting, and show interface and typed-semantics
25-
identities in the Playground Inspector.
27+
identities in the Playground Inspector. Route all wide u128 binary operands
28+
through shared stack-spilled loading so dynamic Molecule-table field
29+
validation can no longer clobber live limbs, keep constant folding from
30+
wrapping arithmetic the runtime would trap on, decide match exhaustiveness
31+
with a bounded constructor-matrix computation that merges nested and
32+
or-pattern payload coverage, derive Cell-backed value abilities structurally
33+
through struct and enum fields, and register E2501 with the shared error
34+
registry. Dynamic-schema u128 bitwise, addition, and shift paths now carry
35+
exact CKB-VM execution vectors.
2636

2737
- Ship the 0.24 package and Registry trust closure, informed by Sui Move's
2838
package-alt separation of resolution from compilation. Replace permissive

‎Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ exclude = [
3939
".idea/",
4040
".playwright-mcp/",
4141
".rustfmt.toml",
42+
"audit_poc/",
4243
"rust-toolchain.toml",
4344
".github/",
4445
"AGENTS.md",

‎audit_poc/Cell.toml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[package]
2+
edition = "2026"
3+
name = "auditpoc"
4+
version = "0.1.0"

‎audit_poc/hidden_cell.cell‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// audit: hidden-cell laundering attempt via plain struct wrapper
2+
module audit::hidden_cell
3+
4+
resource Token has store, create, consume, replace, burn {
5+
amount: u128,
6+
}
7+
8+
struct Wrapper has fixed, serializable {
9+
token: Token,
10+
}
11+
12+
struct Box<T: fixed + serializable + non_linear>
13+
has fixed, serializable, non_linear
14+
{
15+
value: T,
16+
}
17+
18+
action verify() -> u64 {
19+
verification
20+
let boxed: Box<Wrapper> = Box<Wrapper> { value: Wrapper { token: ... } }
21+
0
22+
}

‎audit_poc/nested_exhaustive.cell‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// audit: exhaustive nested-variant match should be accepted
2+
module audit::nested_patterns
3+
4+
enum Inner { None, Some((u64, u64)) }
5+
enum Outer { Empty, Wrapped(Inner) }
6+
7+
action verify() -> u64 {
8+
verification
9+
let outer: Outer = Outer::Wrapped(Inner::Some((20, 22)))
10+
match outer {
11+
Outer::Empty => { 0 }
12+
Outer::Wrapped(Inner::None) => { 1 }
13+
Outer::Wrapped(Inner::Some((left, right))) => { left + right }
14+
}
15+
}

‎audit_poc/or_pattern_payload.cell‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// audit: payload or-pattern covering all variants should be accepted
2+
module audit::or_pattern
3+
4+
enum Inner { None, Some((u64, u64)) }
5+
enum Outer { Empty, Wrapped(Inner) }
6+
7+
action verify() -> u64 {
8+
verification
9+
let outer: Outer = Outer::Wrapped(Inner::None)
10+
match outer {
11+
Outer::Empty => { 0 }
12+
Outer::Wrapped(Inner::None | Inner::Some(_)) => { 1 }
13+
}
14+
}

‎audit_poc/src/main.cell‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// dynamic-layout struct forces Molecule-table field decoding for u128 limbs
2+
module audit::dyn_struct_u128
3+
4+
struct Entry {
5+
amount: u128,
6+
note: Vec<u8>,
7+
}
8+
9+
action verify(e1: Entry, e2: Entry) -> u64 {
10+
verification
11+
let masked: u128 = e1.amount & e2.amount
12+
let shifted: u128 = e1.amount << 3
13+
0
14+
}

‎crates/cellscript-artifact-checker/src/checker.rs‎

Lines changed: 1168 additions & 27 deletions
Large diffs are not rendered by default.

‎crates/cellscript-artifact-checker/src/schema.rs‎

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use serde::{Deserialize, Serialize};
22

3-
pub const LOWERING_RECORD_SCHEMA: &str = "cellscript-verified-lowering-record-v2";
4-
pub const TYPED_SEMANTICS_SCHEMA: &str = "cellscript-typed-semantics-v1";
3+
pub const LOWERING_RECORD_SCHEMA: &str = "cellscript-verified-lowering-record-v3";
4+
pub const TYPED_SEMANTICS_SCHEMA: &str = "cellscript-typed-semantics-v2";
55
pub const SOURCE_MAP_SCHEMA: &str = "cellscript-source-artifact-map-v1";
66
pub const CHECKER_POLICY_SCHEMA: &str = "cellscript-artifact-checker-policy-v1";
77
pub const CHECKER_REPORT_SCHEMA: &str = "cellscript-artifact-checker-report-v1";
8-
pub const LOWERING_RECORD_VERSION: u32 = 2;
8+
pub const LOWERING_RECORD_VERSION: u32 = 3;
9+
pub const TYPED_SEMANTICS_VERSION: u32 = 2;
910
pub const SOURCE_MAP_VERSION: u32 = 1;
1011
pub const CHECKER_VERSION: &str = env!("CARGO_PKG_VERSION");
1112

@@ -68,6 +69,11 @@ impl VerifiedLoweringRecord {
6869
entry.proof_ids.dedup();
6970
entry.capabilities.sort();
7071
entry.capabilities.dedup();
72+
entry.typed_blocks.sort_by_key(|block| block.id);
73+
for block in &mut entry.typed_blocks {
74+
block.machine_block_ids.sort();
75+
block.machine_block_ids.dedup();
76+
}
7177
}
7278
self.blocks.sort_by(|a, b| a.id.cmp(&b.id));
7379
for block in &mut self.blocks {
@@ -104,6 +110,10 @@ impl TypedSemanticRecord {
104110
self.types.sort_by(|left, right| left.name.cmp(&right.name));
105111
for ty in &mut self.types {
106112
ty.fields.sort_by(|left, right| left.offset.cmp(&right.offset).then(left.name.cmp(&right.name)));
113+
ty.variants.sort_by(|left, right| left.tag.cmp(&right.tag).then(left.name.cmp(&right.name)));
114+
for variant in &mut ty.variants {
115+
variant.fields.sort_by_key(|field| field.index);
116+
}
107117
ty.capabilities.sort();
108118
ty.capabilities.dedup();
109119
}
@@ -133,7 +143,10 @@ pub struct TypedSemanticType {
133143
pub kind: String,
134144
pub encoded_size: Option<u32>,
135145
pub fields: Vec<TypedSemanticField>,
146+
pub tag_width_bytes: Option<u32>,
147+
pub variants: Vec<TypedSemanticVariant>,
136148
pub capabilities: Vec<String>,
149+
pub identity_policy: String,
137150
pub layout_hash: String,
138151
}
139152

@@ -146,6 +159,25 @@ pub struct TypedSemanticField {
146159
pub width_bytes: Option<u32>,
147160
}
148161

162+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
163+
#[serde(deny_unknown_fields)]
164+
pub struct TypedSemanticVariant {
165+
pub name: String,
166+
pub tag: u32,
167+
pub payload_width_bytes: u32,
168+
pub fields: Vec<TypedSemanticVariantField>,
169+
}
170+
171+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
172+
#[serde(deny_unknown_fields)]
173+
pub struct TypedSemanticVariantField {
174+
pub index: u32,
175+
pub ty: String,
176+
pub offset: u32,
177+
pub width_bytes: u32,
178+
pub linear: bool,
179+
}
180+
149181
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
150182
#[serde(deny_unknown_fields)]
151183
pub struct TypedSemanticEntry {
@@ -155,6 +187,7 @@ pub struct TypedSemanticEntry {
155187
pub params: Vec<TypedSemanticParam>,
156188
pub return_type: String,
157189
pub effect: String,
190+
pub entry_block: u32,
158191
pub locals: Vec<TypedSemanticLocal>,
159192
pub blocks: Vec<TypedSemanticBlock>,
160193
pub borrows: Vec<TypedSemanticBorrow>,
@@ -178,6 +211,7 @@ pub struct TypedSemanticParam {
178211
#[serde(deny_unknown_fields)]
179212
pub struct TypedSemanticLocal {
180213
pub id: u32,
214+
pub source_id: u64,
181215
pub name: String,
182216
pub ty: String,
183217
}
@@ -189,14 +223,24 @@ pub struct TypedSemanticBlock {
189223
pub operations: Vec<TypedSemanticOperation>,
190224
pub successors: Vec<u32>,
191225
pub terminator: String,
226+
pub runtime_error: Option<TypedSemanticRuntimeError>,
227+
}
228+
229+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
230+
#[serde(deny_unknown_fields)]
231+
pub struct TypedSemanticRuntimeError {
232+
pub code: u64,
233+
pub name: String,
192234
}
193235

194236
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
195237
#[serde(deny_unknown_fields)]
196238
pub struct TypedSemanticOperation {
239+
pub index: u32,
197240
pub opcode: String,
198241
pub destinations: Vec<u32>,
199242
pub operands: Vec<TypedSemanticOperand>,
243+
pub detail: TypedSemanticOperationDetail,
200244
pub call: Option<TypedSemanticCall>,
201245
}
202246

@@ -205,6 +249,90 @@ pub struct TypedSemanticOperation {
205249
pub struct TypedSemanticOperand {
206250
pub local: Option<u32>,
207251
pub ty: String,
252+
pub constant: Option<TypedSemanticConstant>,
253+
}
254+
255+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
256+
#[serde(tag = "kind", rename_all = "kebab-case", deny_unknown_fields)]
257+
pub enum TypedSemanticOperationDetail {
258+
#[default]
259+
None,
260+
Constant {
261+
value: TypedSemanticConstant,
262+
},
263+
Binding {
264+
name: String,
265+
},
266+
BinaryOperator {
267+
operator: String,
268+
},
269+
UnaryOperator {
270+
operator: String,
271+
},
272+
Field {
273+
name: String,
274+
},
275+
Collection {
276+
declared_type: String,
277+
},
278+
Reference {
279+
declared_type: String,
280+
},
281+
EnumConstruct {
282+
enum_name: String,
283+
variant: String,
284+
},
285+
EnumTag {
286+
enum_name: String,
287+
},
288+
EnumPayload {
289+
enum_name: String,
290+
variant: String,
291+
field_index: u32,
292+
},
293+
Create {
294+
pattern: TypedSemanticCreatePattern,
295+
},
296+
Destroy {
297+
policy: String,
298+
},
299+
CreateUnique {
300+
pattern: TypedSemanticCreatePattern,
301+
identity: String,
302+
},
303+
ReplaceUnique {
304+
pattern: TypedSemanticCreatePattern,
305+
identity: String,
306+
},
307+
CellMetadata {
308+
field: String,
309+
},
310+
}
311+
312+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
313+
#[serde(deny_unknown_fields)]
314+
pub struct TypedSemanticCreatePattern {
315+
pub operation: String,
316+
pub ty: String,
317+
pub binding: String,
318+
pub field_names: Vec<String>,
319+
pub has_lock: bool,
320+
pub identity: String,
321+
}
322+
323+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
324+
#[serde(tag = "kind", content = "value", rename_all = "kebab-case", deny_unknown_fields)]
325+
pub enum TypedSemanticConstant {
326+
Unit,
327+
U8(String),
328+
U16(String),
329+
U32(String),
330+
U64(String),
331+
U128(String),
332+
Bool(bool),
333+
Address(String),
334+
Hash(String),
335+
Array(Vec<TypedSemanticConstant>),
208336
}
209337

210338
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
@@ -225,6 +353,10 @@ pub struct TypedSemanticBorrow {
225353
pub binding: String,
226354
pub root_type: String,
227355
pub view_type: String,
356+
pub start_block: u32,
357+
pub start_operation: u32,
358+
pub end_block: Option<u32>,
359+
pub end_operation: Option<u32>,
228360
pub escapes: bool,
229361
}
230362

@@ -242,10 +374,16 @@ pub struct TypedSemanticOwnership {
242374
#[serde(deny_unknown_fields)]
243375
pub struct TypedSemanticInstantiation {
244376
pub kind: String,
377+
pub module: String,
245378
pub template: String,
379+
pub concrete_name: String,
246380
pub identity: String,
247381
pub type_arguments: Vec<String>,
382+
pub value_ability_registry_version: u32,
248383
pub constraints_verified: bool,
384+
pub fixed_layout_required: bool,
385+
pub cell_backed_layout_rejected: bool,
386+
pub identity_includes_phantom_arguments: bool,
249387
}
250388

251389
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -262,6 +400,15 @@ pub struct LoweringEntry {
262400
pub proof_ids: Vec<String>,
263401
pub frame_size_bytes: u32,
264402
pub outgoing_argument_bytes: u32,
403+
pub typed_blocks: Vec<TypedBlockBinding>,
404+
}
405+
406+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
407+
#[serde(deny_unknown_fields)]
408+
pub struct TypedBlockBinding {
409+
pub id: u32,
410+
pub hash: String,
411+
pub machine_block_ids: Vec<String>,
265412
}
266413

267414
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
@@ -302,6 +449,7 @@ pub struct LoweringBlock {
302449
pub owner_entry: String,
303450
pub reachable: bool,
304451
pub lowering_block_id: Option<u32>,
452+
pub typed_block_hash: Option<String>,
305453
pub machine_label: Option<String>,
306454
pub terminator: MachineTerminator,
307455
pub range: MachineRange,

0 commit comments

Comments
 (0)