feat(codegen): stack under/overflow guards in compiled words (WS-007)
Compiled code could silently move dsp/rsp/fsp out of their stack regions (e.g. DROP on an empty stack), corrupting later pushes with no diagnostic -- the addresses stay inside valid linear memory, so nothing could trap. Host-side checks cannot catch it. - Guards are emitted at the sp-adjustment choke points (dsp_inc/ dsp_dec, fsp_inc/fsp_dec, rpush/rpop/rpeek, peek, TwoDup/TwoDrop, promoted prologue/epilogue -- DROP never loads its value, so guarding pop() alone is not enough). On fault: write the code to SYSVAR_FAULT_CODE, call _STACK_FAULT_, which THROWs it -- so guards are CATCHable and print standard messages (-3/-4/-5/-6/ -44/-45). - The batch/consolidated compile path (all boot primitives) and the export path are wired too; a thread-local carries the fault index into the shared emission helpers. - Config: codegen.stack_guards, default ON. `wafer build` output defaults OFF (production artifact); WAFER_STACK_GUARDS=0|1 overrides either. Perf comparison lanes run unguarded. - Measured overhead in release loops: within noise (never-taken branches). - toolstest.fth baseline 37 -> 38: line 368's bare interpreted `R>` used to underflow silently and count as passing; the guard now correctly reports -6.
This commit is contained in:
+15
-2
@@ -137,7 +137,8 @@ fn cmd_build(
|
||||
) -> anyhow::Result<()> {
|
||||
let source = std::fs::read_to_string(file)?;
|
||||
|
||||
let mut vm = ForthVM::<NativeRuntime>::new()?;
|
||||
// Exported modules are production artifacts: no stack guards by default
|
||||
let mut vm = ForthVM::<NativeRuntime>::new_with_config(vm_config(false))?;
|
||||
vm.set_recording(true);
|
||||
vm.evaluate(&source)?;
|
||||
|
||||
@@ -260,9 +261,21 @@ fn cmd_run(file: &str) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `WaferConfig` for CLI-created VMs. `WAFER_STACK_GUARDS=0|1` overrides
|
||||
/// the per-command default (REPL/file execution on, build off).
|
||||
fn vm_config(default_guards: bool) -> wafer_core::config::WaferConfig {
|
||||
let mut cfg = wafer_core::config::WaferConfig::all();
|
||||
cfg.codegen.stack_guards = match std::env::var("WAFER_STACK_GUARDS").ok().as_deref() {
|
||||
Some("0") => false,
|
||||
Some(_) => true,
|
||||
None => default_guards,
|
||||
};
|
||||
cfg
|
||||
}
|
||||
|
||||
/// `wafer` (REPL) or `wafer program.fth` (evaluate and exit)
|
||||
fn cmd_eval_or_repl(file: Option<&str>) -> anyhow::Result<()> {
|
||||
let mut vm = ForthVM::<NativeRuntime>::new()?;
|
||||
let mut vm = ForthVM::<NativeRuntime>::new_with_config(vm_config(true))?;
|
||||
|
||||
match file {
|
||||
Some(file) => {
|
||||
|
||||
Reference in New Issue
Block a user