This commit is contained in:
bQUARKz 2026-02-19 12:40:15 +00:00
parent 546a4f219b
commit 7ba1d71b69
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
2 changed files with 38 additions and 52 deletions

View File

@ -14,6 +14,7 @@ pub enum VerifierError {
InvalidJumpTarget { pc: usize, target: usize }, InvalidJumpTarget { pc: usize, target: usize },
JumpToMidInstruction { pc: usize, target: usize }, JumpToMidInstruction { pc: usize, target: usize },
StackUnderflow { pc: usize, opcode: OpCode }, StackUnderflow { pc: usize, opcode: OpCode },
StackOverflow { pc: usize, height: u16, limit: u16 },
StackMismatchJoin { pc: usize, target: usize, height_in: u16, height_target: u16 }, StackMismatchJoin { pc: usize, target: usize, height_in: u16, height_target: u16 },
BadRetStackHeight { pc: usize, height: u16, expected: u16 }, BadRetStackHeight { pc: usize, height: u16, expected: u16 },
FunctionOutOfBounds { func_idx: usize, start: usize, end: usize, code_len: usize }, FunctionOutOfBounds { func_idx: usize, start: usize, end: usize, code_len: usize },
@ -143,6 +144,15 @@ impl Verifier {
let out_height = in_height - pops + pushes; let out_height = in_height - pops + pushes;
max_stack = max_stack.max(out_height); max_stack = max_stack.max(out_height);
// Enforce declared max stack if provided (0 means "no explicit limit")
if func.max_stack_slots != 0 && out_height > func.max_stack_slots {
return Err(VerifierError::StackOverflow {
pc: func_start + pc,
height: out_height,
limit: func.max_stack_slots,
});
}
if instr.opcode == OpCode::Ret { if instr.opcode == OpCode::Ret {
if in_height != func.return_slots { if in_height != func.return_slots {
return Err(VerifierError::BadRetStackHeight { return Err(VerifierError::BadRetStackHeight {
@ -434,6 +444,34 @@ mod tests {
assert_eq!(res[0], 2); assert_eq!(res[0], 2);
} }
#[test]
fn test_verifier_stack_overflow_limit() {
// Same program as max_stack, but declare max_stack_slots = 1 → should overflow at second push
let mut code = Vec::new();
code.push(OpCode::PushI32 as u8);
code.push(0x00);
code.extend_from_slice(&1u32.to_le_bytes());
code.push(OpCode::PushI32 as u8);
code.push(0x00);
code.extend_from_slice(&2u32.to_le_bytes());
code.push(OpCode::Add as u8);
code.push(0x00);
code.push(OpCode::Ret as u8);
code.push(0x00);
let functions = vec![FunctionMeta {
code_offset: 0,
code_len: 16,
return_slots: 1,
max_stack_slots: 1,
..Default::default()
}];
let res = Verifier::verify(&code, &functions);
// Overflow occurs at pc = 6 (start of second PushI32)
assert_eq!(res, Err(VerifierError::StackOverflow { pc: 6, height: 2, limit: 1 }));
}
#[test] #[test]
fn test_verifier_invalid_syscall_id() { fn test_verifier_invalid_syscall_id() {
let mut code = Vec::new(); let mut code = Vec::new();

View File

@ -1,55 +1,3 @@
# PR-4.2 — Basic Stack Safety Verification (Underflow/Overflow)
### Briefing
The verifier must ensure the stack never underflows or exceeds defined limits during execution.
### Target
* Implement stack safety checks across the control-flow graph.
### Work items
* Simulate stack depth across instructions.
* Detect:
* Stack underflow.
* Stack overflow beyond declared limits.
* Emit appropriate verifier errors.
### Acceptance checklist
* [ ] Underflow cases are rejected.
* [ ] Overflow cases are rejected.
* [ ] Valid programs pass.
* [ ] `cargo test` passes.
### Tests
* Add tests:
* Program with underflow → verifier error.
* Program exceeding stack limit → verifier error.
* Valid program → passes.
### Junie instructions
**You MAY:**
* Add stack depth simulation.
* Introduce verifier error types.
**You MUST NOT:**
* Change runtime stack implementation.
* Introduce dynamic stack resizing.
**If unclear:**
* Ask before choosing stack limit rules.
---
# PR-4.3 — Control Flow and Jump Target Verification # PR-4.3 — Control Flow and Jump Target Verification
### Briefing ### Briefing