pr7.8
This commit is contained in:
parent
d123919b73
commit
b48386b154
@ -32,6 +32,10 @@ pub enum VerifierError {
|
|||||||
UnknownClosureCallee { pc: usize },
|
UnknownClosureCallee { pc: usize },
|
||||||
/// User-provided arg_count for CALL_CLOSURE does not match callee signature
|
/// User-provided arg_count for CALL_CLOSURE does not match callee signature
|
||||||
BadClosureArgCount { pc: usize, expected: u16, got: u16 },
|
BadClosureArgCount { pc: usize, expected: u16, got: u16 },
|
||||||
|
/// YIELD executed in an invalid context (minimal safety rule violation)
|
||||||
|
InvalidYieldContext { pc: usize, height: u16 },
|
||||||
|
/// SPAWN arg_count does not match callee param_slots
|
||||||
|
BadSpawnArgCount { pc: usize, expected: u16, got: u16 },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Verifier;
|
pub struct Verifier;
|
||||||
@ -168,6 +172,23 @@ impl Verifier {
|
|||||||
// Temporarily set pushes=0; we'll compute real pushes after type checks.
|
// Temporarily set pushes=0; we'll compute real pushes after type checks.
|
||||||
(arg_count + 1, 0)
|
(arg_count + 1, 0)
|
||||||
}
|
}
|
||||||
|
OpCode::Spawn => {
|
||||||
|
// imm: fn_id (u32), arg_count (u32)
|
||||||
|
let (fn_id, arg_count_u32) = instr.imm_u32x2().unwrap();
|
||||||
|
let callee = all_functions.get(fn_id as usize).ok_or_else(|| {
|
||||||
|
VerifierError::InvalidFuncId { pc: func_start + pc, id: fn_id }
|
||||||
|
})?;
|
||||||
|
let arg_count = arg_count_u32 as u16;
|
||||||
|
// Enforce exact arity match for safety and determinism
|
||||||
|
if callee.param_slots != arg_count {
|
||||||
|
return Err(VerifierError::BadSpawnArgCount {
|
||||||
|
pc: func_start + pc,
|
||||||
|
expected: callee.param_slots,
|
||||||
|
got: arg_count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(arg_count, 0)
|
||||||
|
}
|
||||||
OpCode::Ret => (func.return_slots, 0),
|
OpCode::Ret => (func.return_slots, 0),
|
||||||
OpCode::Syscall => {
|
OpCode::Syscall => {
|
||||||
let id = instr.imm_u32().unwrap();
|
let id = instr.imm_u32().unwrap();
|
||||||
@ -186,6 +207,13 @@ impl Verifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coroutine safety: forbid YIELD when operand stack is not empty (minimal rule)
|
||||||
|
if let OpCode::Yield = instr.opcode {
|
||||||
|
if in_height != 0 {
|
||||||
|
return Err(VerifierError::InvalidYieldContext { pc: func_start + pc, height: in_height });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compute out types vector with closure-aware rules
|
// Compute out types vector with closure-aware rules
|
||||||
use SlotTy::*;
|
use SlotTy::*;
|
||||||
let mut out_types = in_types.clone();
|
let mut out_types = in_types.clone();
|
||||||
|
|||||||
55
crates/console/prometeu-vm/tests/verifier_coroutines.rs
Normal file
55
crates/console/prometeu-vm/tests/verifier_coroutines.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use prometeu_bytecode::FunctionMeta;
|
||||||
|
use prometeu_bytecode::isa::core::CoreOpCode as OpCode;
|
||||||
|
use prometeu_vm::verifier::{Verifier, VerifierError};
|
||||||
|
|
||||||
|
fn enc_op(op: OpCode) -> [u8; 2] { [(op as u8), 0x00] }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn err_invalid_yield_with_non_empty_stack() {
|
||||||
|
// Program:
|
||||||
|
// 0..2: PUSH_I32 1 (imm 4 bytes)
|
||||||
|
// 6..8: YIELD
|
||||||
|
// 8..10: RET
|
||||||
|
let mut code = Vec::new();
|
||||||
|
code.extend_from_slice(&enc_op(OpCode::PushI32));
|
||||||
|
code.extend_from_slice(&1u32.to_le_bytes());
|
||||||
|
code.extend_from_slice(&enc_op(OpCode::Yield));
|
||||||
|
code.extend_from_slice(&enc_op(OpCode::Ret));
|
||||||
|
|
||||||
|
let functions = vec![FunctionMeta { code_offset: 0, code_len: code.len() as u32, return_slots: 1, ..Default::default() }];
|
||||||
|
let res = Verifier::verify(&code, &functions);
|
||||||
|
assert_eq!(res, Err(VerifierError::InvalidYieldContext { pc: 6, height: 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn err_spawn_arg_mismatch() {
|
||||||
|
// Caller at func 0: SPAWN fn_id=1, arg_count=1; RET
|
||||||
|
// Callee at func 1: expects 2 param_slots
|
||||||
|
let mut code = Vec::new();
|
||||||
|
code.extend_from_slice(&enc_op(OpCode::Spawn));
|
||||||
|
code.extend_from_slice(&1u32.to_le_bytes()); // fn_id
|
||||||
|
code.extend_from_slice(&1u32.to_le_bytes()); // arg_count (mismatch: callee expects 2)
|
||||||
|
code.extend_from_slice(&enc_op(OpCode::Ret));
|
||||||
|
|
||||||
|
let caller = FunctionMeta { code_offset: 0, code_len: code.len() as u32, return_slots: 0, ..Default::default() };
|
||||||
|
// Callee has no code here; only signature matters
|
||||||
|
let callee = FunctionMeta { code_offset: code.len() as u32, code_len: 0, param_slots: 2, return_slots: 0, ..Default::default() };
|
||||||
|
let functions = vec![caller, callee];
|
||||||
|
|
||||||
|
let res = Verifier::verify(&code, &functions);
|
||||||
|
assert_eq!(res, Err(VerifierError::BadSpawnArgCount { pc: 0, expected: 2, got: 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn err_sleep_truncated_immediate() {
|
||||||
|
// Encode SLEEP but provide only 1 of 4 immediate bytes
|
||||||
|
let mut code = Vec::new();
|
||||||
|
code.extend_from_slice(&enc_op(OpCode::Sleep));
|
||||||
|
code.push(0xAB);
|
||||||
|
let functions = vec![FunctionMeta { code_offset: 0, code_len: code.len() as u32, ..Default::default() }];
|
||||||
|
let res = Verifier::verify(&code, &functions);
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
Err(VerifierError::TruncatedImmediate { pc: 0, opcode: OpCode::Sleep, need: 4, have: 1 })
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,32 +1,3 @@
|
|||||||
# PR-7.8 — Verifier Rules
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
Verifier must enforce coroutine safety.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
* YIELD forbidden inside invalid contexts (define minimal safe rule).
|
|
||||||
* SPAWN argument validation.
|
|
||||||
* SLEEP argument type validation.
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
* [ ] Invalid YIELD rejected.
|
|
||||||
* [ ] SPAWN arg mismatch rejected.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
* Invalid bytecode rejected.
|
|
||||||
|
|
||||||
## Junie Rules
|
|
||||||
|
|
||||||
You MUST NOT weaken verifier.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PR-7.9 — Determinism & Stress Tests
|
# PR-7.9 — Determinism & Stress Tests
|
||||||
|
|
||||||
## Briefing
|
## Briefing
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user