Skip to content

Commit e72f70e

Browse files
committed
fix: close NovaSeal VM2 ABI verification gaps
1 parent b0ce3d0 commit e72f70e

3 files changed

Lines changed: 228 additions & 46 deletions

File tree

‎src/codegen/mod.rs‎

Lines changed: 81 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,10 +1416,16 @@ impl CodeGenerator {
14161416
self.emit("add t0, sp, t6");
14171417
self.emit(format!("addi t0, t0, {}", ENTRY_WITNESS_BUFFER_OFFSET));
14181418
if abi_index < 8 {
1419-
self.emit_entry_witness_scalar_load_from_reg(&format!("a{}", abi_index), "t0", width, param.ty == IrType::I32);
1419+
self.emit_entry_witness_scalar_load_from_reg(
1420+
&format!("a{}", abi_index),
1421+
"t0",
1422+
"t1",
1423+
width,
1424+
param.ty == IrType::I32,
1425+
);
14201426
} else {
14211427
let caller_stack_offset = (abi_index - 8) * 8;
1422-
self.emit_entry_witness_scalar_load_from_reg("t3", "t0", width, param.ty == IrType::I32);
1428+
self.emit_entry_witness_scalar_load_from_reg("t3", "t0", "t1", width, param.ty == IrType::I32);
14231429
self.emit(format!(
14241430
"# cellscript entry abi: scalar param {} stored to caller stack +{}",
14251431
param.name, caller_stack_offset
@@ -1628,14 +1634,24 @@ impl CodeGenerator {
16281634
}
16291635
}
16301636

1631-
fn emit_entry_witness_scalar_load_from_reg(&mut self, dest_reg: &str, base_reg: &str, width: usize, signed_i32: bool) {
1637+
fn emit_entry_witness_scalar_load_from_reg(
1638+
&mut self,
1639+
dest_reg: &str,
1640+
base_reg: &str,
1641+
byte_reg: &str,
1642+
width: usize,
1643+
signed_i32: bool,
1644+
) {
1645+
debug_assert_ne!(dest_reg, base_reg, "entry scalar decoder destination must not alias its base");
1646+
debug_assert_ne!(byte_reg, base_reg, "entry scalar decoder scratch must not alias its base");
1647+
debug_assert_ne!(byte_reg, dest_reg, "entry scalar decoder scratch must not alias its destination");
16321648
self.emit(format!("li {}, 0", dest_reg));
16331649
for byte_index in 0..width {
1634-
self.emit(format!("lbu t0, {}({})", byte_index, base_reg));
1650+
self.emit(format!("lbu {}, {}({})", byte_reg, byte_index, base_reg));
16351651
if byte_index != 0 {
1636-
self.emit(format!("slli t0, t0, {}", byte_index * 8));
1652+
self.emit(format!("slli {}, {}, {}", byte_reg, byte_reg, byte_index * 8));
16371653
}
1638-
self.emit(format!("or {}, {}, t0", dest_reg, dest_reg));
1654+
self.emit(format!("or {}, {}, {}", dest_reg, dest_reg, byte_reg));
16391655
}
16401656
if signed_i32 {
16411657
self.emit_sign_extend_i32(dest_reg);
@@ -1653,14 +1669,17 @@ impl CodeGenerator {
16531669
}
16541670
}
16551671

1656-
fn emit_entry_load_u32_from_reg(&mut self, dest_reg: &str, base_reg: &str) {
1672+
fn emit_entry_load_u32_from_reg(&mut self, dest_reg: &str, base_reg: &str, byte_reg: &str) {
1673+
debug_assert_ne!(dest_reg, base_reg, "entry u32 decoder destination must not alias its base");
1674+
debug_assert_ne!(byte_reg, base_reg, "entry u32 decoder scratch must not alias its base");
1675+
debug_assert_ne!(byte_reg, dest_reg, "entry u32 decoder scratch must not alias its destination");
16571676
self.emit(format!("li {}, 0", dest_reg));
16581677
for byte_index in 0..4 {
1659-
self.emit(format!("lbu t0, {}({})", byte_index, base_reg));
1678+
self.emit(format!("lbu {}, {}({})", byte_reg, byte_index, base_reg));
16601679
if byte_index != 0 {
1661-
self.emit(format!("slli t0, t0, {}", byte_index * 8));
1680+
self.emit(format!("slli {}, {}, {}", byte_reg, byte_reg, byte_index * 8));
16621681
}
1663-
self.emit(format!("or {}, {}, t0", dest_reg, dest_reg));
1682+
self.emit(format!("or {}, {}, {}", dest_reg, dest_reg, byte_reg));
16641683
}
16651684
}
16661685

@@ -1718,14 +1737,14 @@ impl CodeGenerator {
17181737

17191738
self.emit_sp_addi("t0", ENTRY_SCRIPT_BUFFER_OFFSET);
17201739
self.emit("add t0, t0, t4");
1721-
self.emit_entry_load_u32_from_reg("t5", "t0");
1740+
self.emit_entry_load_u32_from_reg("t5", "t0", "t1");
17221741
self.emit("addi t6, t4, 4");
17231742
self.emit("add t1, t6, t5");
17241743
self.emit("sltu t2, t3, t1");
17251744
self.emit(format!("beqz t2, {}", args_span_ok_label));
17261745
self.emit(format!("j {}", fail_label));
17271746
self.emit_label(&args_span_ok_label);
1728-
self.emit_stack_store("t6", ENTRY_SCRIPT_ARGS_START_OFFSET);
1747+
self.emit_stack_store_with_avoid("t6", ENTRY_SCRIPT_ARGS_START_OFFSET, &["t5"]);
17291748
self.emit_stack_store("t5", ENTRY_SCRIPT_ARGS_LEN_OFFSET);
17301749
self.emit("li t0, 0");
17311750
self.emit_stack_store("t0", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
@@ -1749,34 +1768,36 @@ impl CodeGenerator {
17491768
};
17501769
let bytes_ok_label = self.fresh_label("entry_lock_args_bytes_ok");
17511770
self.emit(format!("# cellscript entry abi: lock_args param {} consumes {} script arg byte(s)", param.name, width));
1752-
self.emit_stack_load("t6", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
1753-
self.emit_stack_load("t5", ENTRY_SCRIPT_ARGS_LEN_OFFSET);
1754-
self.emit(format!("addi t1, t6, {}", width));
1755-
self.emit("sltu t2, t5, t1");
1771+
let witness_cursor_live = ["t5", "t6"];
1772+
let witness_and_script_cursor_live = ["t3", "t5", "t6"];
1773+
self.emit_stack_load_with_avoid("t3", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET, &witness_cursor_live);
1774+
self.emit_stack_load_with_avoid("t4", ENTRY_SCRIPT_ARGS_LEN_OFFSET, &witness_and_script_cursor_live);
1775+
self.emit(format!("addi t1, t3, {}", width));
1776+
self.emit("sltu t2, t4, t1");
17561777
self.emit(format!("beqz t2, {}", bytes_ok_label));
17571778
self.emit(format!("j {}", fail_label));
17581779
self.emit_label(&bytes_ok_label);
1759-
self.emit_stack_load("t3", ENTRY_SCRIPT_ARGS_START_OFFSET);
1760-
self.emit("add t3, t3, t6");
1780+
self.emit_stack_load_with_avoid("t4", ENTRY_SCRIPT_ARGS_START_OFFSET, &witness_and_script_cursor_live);
1781+
self.emit("add t4, t4, t3");
17611782
self.emit_sp_addi("t0", ENTRY_SCRIPT_BUFFER_OFFSET);
1762-
self.emit("add t0, t0, t3");
1783+
self.emit("add t0, t0, t4");
17631784

17641785
if fixed_byte_width.is_some() {
17651786
self.emit_entry_abi_reg_arg(*abi_index, "t0", outgoing_stack_arg_bytes);
17661787
self.emit_entry_abi_immediate_arg(*abi_index + 1, width as u64, outgoing_stack_arg_bytes);
17671788
*abi_index += 2;
17681789
} else if *abi_index < 8 {
1769-
self.emit_entry_witness_scalar_load_from_reg(&format!("a{}", *abi_index), "t0", width, param.ty == IrType::I32);
1790+
self.emit_entry_witness_scalar_load_from_reg(&format!("a{}", *abi_index), "t0", "t1", width, param.ty == IrType::I32);
17701791
*abi_index += 1;
17711792
} else {
1772-
self.emit_entry_witness_scalar_load_from_reg("t4", "t0", width, param.ty == IrType::I32);
1793+
self.emit_entry_witness_scalar_load_from_reg("t4", "t0", "t1", width, param.ty == IrType::I32);
17731794
self.emit_entry_abi_reg_arg(*abi_index, "t4", outgoing_stack_arg_bytes);
17741795
*abi_index += 1;
17751796
}
17761797

1777-
self.emit_stack_load("t6", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
1778-
self.emit(format!("addi t6, t6, {}", width));
1779-
self.emit_stack_store("t6", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
1798+
self.emit_stack_load_with_avoid("t3", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET, &witness_cursor_live);
1799+
self.emit(format!("addi t3, t3, {}", width));
1800+
self.emit_stack_store_with_avoid("t3", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET, &witness_cursor_live);
17801801
}
17811802

17821803
fn emit_entry_lock_args_exact_size_check(&mut self, fail_label: &str) {
@@ -3436,7 +3457,12 @@ impl CodeGenerator {
34363457

34373458
/// Emit `ld rd, offset(sp)` through the centralized stack-offset gate.
34383459
fn emit_stack_load(&mut self, rd: &str, offset: usize) {
3439-
self.emit_stack_access("ld", rd, offset);
3460+
self.emit_stack_access_with_avoid("ld", rd, offset, &[]);
3461+
}
3462+
3463+
/// Emit `ld rd, offset(sp)` without clobbering explicitly live registers.
3464+
fn emit_stack_load_with_avoid(&mut self, rd: &str, offset: usize, avoid: &[&str]) {
3465+
self.emit_stack_access_with_avoid("ld", rd, offset, avoid);
34403466
}
34413467

34423468
/// Emit `lbu rd, offset(sp)` through the centralized stack-offset gate.
@@ -3446,7 +3472,12 @@ impl CodeGenerator {
34463472

34473473
/// Emit `sd rs2, offset(sp)` through the centralized stack-offset gate.
34483474
fn emit_stack_store(&mut self, rs2: &str, offset: usize) {
3449-
self.emit_stack_access("sd", rs2, offset);
3475+
self.emit_stack_access_with_avoid("sd", rs2, offset, &[]);
3476+
}
3477+
3478+
/// Emit `sd rs2, offset(sp)` without clobbering explicitly live registers.
3479+
fn emit_stack_store_with_avoid(&mut self, rs2: &str, offset: usize, avoid: &[&str]) {
3480+
self.emit_stack_access_with_avoid("sd", rs2, offset, avoid);
34503481
}
34513482

34523483
/// Emit `sb rs2, offset(sp)` through the centralized stack-offset gate.
@@ -3455,11 +3486,18 @@ impl CodeGenerator {
34553486
}
34563487

34573488
fn emit_stack_access(&mut self, opcode: &str, register: &str, offset: usize) {
3489+
self.emit_stack_access_with_avoid(opcode, register, offset, &[]);
3490+
}
3491+
3492+
fn emit_stack_access_with_avoid(&mut self, opcode: &str, register: &str, offset: usize, avoid: &[&str]) {
34583493
let offset = i64::try_from(offset).expect("stack offset should fit in i64");
34593494
if small_signed_immediate(offset) {
34603495
self.emit(format!("{} {}, {}(sp)", opcode, register, offset));
34613496
} else {
3462-
let scratch = scratch_register_avoiding(&[register]);
3497+
let mut live_registers = Vec::with_capacity(1 + avoid.len());
3498+
live_registers.push(register);
3499+
live_registers.extend_from_slice(avoid);
3500+
let scratch = scratch_register_avoiding(&live_registers);
34633501
self.emit(format!("li {}, {}", scratch, offset));
34643502
self.emit(format!("add {}, sp, {}", scratch, scratch));
34653503
self.emit(format!("{} {}, 0({})", opcode, register, scratch));
@@ -3587,7 +3625,7 @@ impl CodeGenerator {
35873625
body.read_refs.iter().enumerate().map(|(index, pattern)| (pattern.binding.as_str(), index)).collect::<HashMap<_, _>>();
35883626
let mut read_ref_param_index = 0usize;
35893627
for param in params {
3590-
if param.source == ParamSource::Output {
3628+
if matches!(param.source, ParamSource::Output | ParamSource::LockArgs) {
35913629
continue;
35923630
}
35933631
if !self.param_is_runtime_bound(param) {
@@ -5646,7 +5684,7 @@ impl CodeGenerator {
56465684
self.emit_loaded_field_equals_expected(size_offset, buffer_offset, layout, expected, context);
56475685
return true;
56485686
}
5649-
let Some(width) = layout_fixed_byte_width(layout) else {
5687+
let Some(width) = layout_fixed_byte_width(layout).or_else(|| self.fixed_named_type_width(&layout.ty)) else {
56505688
return false;
56515689
};
56525690
let Some(source) = self.expected_fixed_byte_source(expected, width) else {
@@ -6356,7 +6394,7 @@ impl CodeGenerator {
63566394
}
63576395
pattern.fields.iter().all(|(field, value)| {
63586396
self.type_layouts.get(&pattern.ty).and_then(|layouts| layouts.get(field)).is_some_and(|layout| {
6359-
if let Some(width) = layout_fixed_byte_width(layout) {
6397+
if let Some(width) = layout_fixed_byte_width(layout).or_else(|| self.fixed_named_type_width(&layout.ty)) {
63606398
self.is_prelude_available_fixed_value(value, width)
63616399
} else {
63626400
self.can_verify_dynamic_create_output_field_value(value, layout)
@@ -6405,7 +6443,7 @@ impl CodeGenerator {
64056443
let Some(layout) = self.type_layouts.get(&pattern.ty).and_then(|fields| fields.get(field)).cloned() else {
64066444
continue;
64076445
};
6408-
if layout_fixed_byte_width(&layout).is_some() {
6446+
if layout_fixed_byte_width(&layout).or_else(|| self.fixed_named_type_width(&layout.ty)).is_some() {
64096447
if is_fixed_type {
64106448
self.emit_loaded_field_bytes_equals_expected(
64116449
size_offset,
@@ -6466,7 +6504,7 @@ impl CodeGenerator {
64666504
field_count: usize,
64676505
expected: &IrOperand,
64686506
) -> bool {
6469-
let Some(width) = layout_fixed_byte_width(layout) else {
6507+
let Some(width) = layout_fixed_byte_width(layout).or_else(|| self.fixed_named_type_width(&layout.ty)) else {
64706508
return false;
64716509
};
64726510
let output_start_offset = self.runtime_expr_temp_offset(0).expect("runtime temp slot 0");
@@ -9730,7 +9768,6 @@ impl CodeGenerator {
97309768
return Ok(());
97319769
};
97329770
let ipc_buffer_offset = self.runtime_scratch_buffer_offset();
9733-
let ipc_size_offset = self.runtime_scratch_size_offset();
97349771
self.emit("# cellscript abi: NovaSeal BIP340 verifier IPC envelope via VM2 pipe/spawn/wait");
97359772
let pipe_ok = self.fresh_label("novaseal_bip340_pipe_ok");
97369773
self.emit("call __ckb_pipe");
@@ -9778,18 +9815,17 @@ impl CodeGenerator {
97789815
self.emit_fail(CellScriptRuntimeError::Bip340SpawnFailed);
97799816
self.emit_label(&spawn_ok);
97809817
self.emit_stack_store("a1", child_pid_offset);
9781-
self.emit("# cellscript abi: BIP340 IPC write canonical 144-byte envelope");
9782-
self.emit("li t0, 144");
9783-
self.emit_stack_store("t0", ipc_size_offset);
9784-
self.emit_stack_load("a0", write_fd_offset);
9785-
self.emit_sp_addi("a1", ipc_buffer_offset);
9786-
self.emit_sp_addi("a2", ipc_size_offset);
9787-
self.emit(format!("li a7, {}", ckb_abi::syscall::WRITE));
9788-
self.emit("ecall");
9789-
let write_ok = self.fresh_label("novaseal_bip340_write_ok");
9790-
self.emit(format!("beqz a0, {}", write_ok));
9791-
self.emit_fail(CellScriptRuntimeError::Bip340MessageWriteFailed);
9792-
self.emit_label(&write_ok);
9818+
self.emit("# cellscript abi: BIP340 IPC write canonical 18-word little-endian envelope");
9819+
for word_index in 0..18 {
9820+
self.emit(format!("# cellscript abi: novaseal bip340 ipc word {}", word_index));
9821+
self.emit_stack_load("a0", write_fd_offset);
9822+
self.emit_stack_load("a1", ipc_buffer_offset + word_index * 8);
9823+
self.emit("call __ckb_pipe_write");
9824+
let write_ok = self.fresh_label("novaseal_bip340_write_ok");
9825+
self.emit(format!("beqz a0, {}", write_ok));
9826+
self.emit_fail(CellScriptRuntimeError::Bip340MessageWriteFailed);
9827+
self.emit_label(&write_ok);
9828+
}
97939829
self.emit_stack_load("a0", write_fd_offset);
97949830
self.emit("call __ckb_close");
97959831
let close_ok = self.fresh_label("novaseal_bip340_close_ok");

0 commit comments

Comments
 (0)