mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Softcode CALL_FUNC is already total (guest error strings, never ECALL_DECLINE). The residual poison was mid-run DBT failure (code buffer full, harvest miss) returning false → full AST re-run after effects. Track eval_ctx::host_ecalls on softcode host ECALLs; after entry, commit #-1 JIT POST-ENTRY FAIL instead of re-running. Document the audit in plan-softcode-post-entry-contract.md and cross-link the Lua plan.
119 lines
4.9 KiB
Markdown
119 lines
4.9 KiB
Markdown
# Softcode post-entry contract (sibling of Lua #1751)
|
|
|
|
**Status:** audit complete 2026-07-29; residual harden shipped with this plan
|
|
**Stake issue:** [#1791](https://github.com/brazilofmux/tinymux/issues/1791)
|
|
**Lua twin:** [`plan-lua-post-entry-contract.md`](plan-lua-post-entry-contract.md)
|
|
|
|
**One-line summary:** Softcode `CALL_FUNC` does **not** soft-decline after partial work. The residual poison path was mid-run **DBT infrastructure** failure → `jit_eval` AST re-run; that is now blocked once any host ECALL has run.
|
|
|
|
---
|
|
|
|
## 0. Why this exists
|
|
|
|
The Lua campaign deleted silent whole-chunk re-run after entry. Its standing
|
|
note assumed softcode had the same property only as a **derived** fact:
|
|
|
|
* depth / invocation watermarks run **before** `dbt_run` (#1002);
|
|
* softcode function ECALLs return `-1` (continue) and put errors in guest
|
|
memory rather than `ECALL_DECLINE`.
|
|
|
|
That left an un-audited gap: what if `dbt_run` itself fails after a host
|
|
ECALL already ran (`pemit`, `setq`, nested `lua()`, …)? Softcode
|
|
`run_cached_program` used to `return false` → `jit_eval` falls through to
|
|
the AST for the **entire** expression — a full re-run, effect-doubling
|
|
risk. Lua's arm already committed errors instead; softcode did not.
|
|
|
|
---
|
|
|
|
## 1. Non-negotiable contract (same shape as Lua)
|
|
|
|
```text
|
|
PRE-ENTRY (nothing user-visible has run yet)
|
|
✓ refuse compile / miss cache / watermarks / oversize cargs → AST OK
|
|
|
|
POST-ENTRY (any host softcode ECALL may have run)
|
|
✓ complete successfully and harvest
|
|
✓ same error string the AST would produce from a finished ECALL
|
|
✓ CPU LIMITED / alarm as AST-shaped diagnostic
|
|
✗ silent whole-expression AST re-run after host work
|
|
```
|
|
|
|
**Failure is allowed; retry of the whole expression after host work is the poison.**
|
|
|
|
---
|
|
|
|
## 2. Audit results (2026-07-29)
|
|
|
|
| Path | Pre- or post-entry? | Behavior | Verdict |
|
|
|------|---------------------|----------|---------|
|
|
| Compile refuse / no tier2 / sandbox | Pre | `jit_eval` → `false` → AST | OK |
|
|
| Depth / invk watermarks | Pre | before `dbt_run` | OK |
|
|
| Oversize `cargs` slots | Pre | before `dbt_run` | OK |
|
|
| `ECALL_CALL_FUNC` / `CALL_INDEX` | Post (once entered) | Error strings in guest; return `-1` continue | **Total** — never `ECALL_DECLINE` |
|
|
| `ECALL_SETQ` / `SETQ_PACK` | Post | Write-through; return `-1` | Total |
|
|
| `ECALL_EXIT` | End | non-negative stops `dbt_run` with success status | OK |
|
|
| Mid-run code buffer full | Post (if any ECALL already ran) | `dbt_run` → `-1` → was AST re-run | **Fixed** (#1791) |
|
|
| Harvest fail after `rc==0` | Post if host ECALLs | was AST re-run | **Fixed** (#1791) |
|
|
| Alarm `-3` | Post | `#-1 CPU LIMITED`, handled, no re-run | OK |
|
|
| Nested `lua()` under softcode | Post | Lua path Phase 4; outer softcode ECALL continues | Lua contract holds |
|
|
|
|
### CALL_FUNC is not the Lua bug class
|
|
|
|
Softcode never had “decline because type surprised us after the callee
|
|
ran.” Builtin and `@function` ECALLs always finish the host call (or write
|
|
a `#-1 …` string) and resume the guest. The surprise was justified.
|
|
|
|
### Residual that *was* real
|
|
|
|
Any `dbt_run` status that `handle_dbt_run_status` does not treat as
|
|
handled (`rc != 0` and not the CPU-limited arm) made softcode
|
|
`run_cached_program` return `false`. Common theoretical trigger:
|
|
**DBT code buffer full mid-translation of a later block** after earlier
|
|
ECALLs already executed (see also #1315 messaging). Rare under normal
|
|
load; still a contract hole.
|
|
|
|
---
|
|
|
|
## 3. Harden shipped with this plan
|
|
|
|
1. `eval_ctx::host_ecalls` — incremented in `ecall_invoke_fun`,
|
|
`ecall_invoke_ufun`, `ECALL_SETQ`, `ECALL_SETQ_PACK`.
|
|
2. Softcode arm after `dbt_run`: if status is unhandled **and**
|
|
`host_ecalls > 0`, commit `#-1 JIT POST-ENTRY FAIL` (or keep an
|
|
existing `#-1…` diagnostic) and **return true** — no AST re-run.
|
|
3. Same rule for harvest failure after a completed run with host work.
|
|
4. Pre-entry and “no host ECALL yet” failures still return `false` so
|
|
AST can answer (correct, effect-free).
|
|
|
|
Lua path unchanged (already Phase 4).
|
|
|
|
---
|
|
|
|
## 4. What we are **not** claiming
|
|
|
|
* Softcode JIT is free of all bugs — only this contract class.
|
|
* Code-buffer-full never happens — only that it must not double effects.
|
|
* Every mid-run failure has a forceable purity test in CI — forcing
|
|
mid-run `dbt` full from softcode is hostile to the suite; the harden
|
|
is structural.
|
|
|
|
---
|
|
|
|
## 5. Residual / follow-ups
|
|
|
|
| Item | Notes |
|
|
|------|--------|
|
|
| Grep for new softcode ECALL cases | Any new host side-effect ECALL must `++host_ecalls` |
|
|
| Optional soak | Drive code buffer pressure under softcode with effectors; assert single delivery |
|
|
| Cross-link | Lua plan § “Standing assumption: the softcode route…” now has this sibling |
|
|
|
|
---
|
|
|
|
## 6. Checklist
|
|
|
|
- [x] CALL_FUNC / CALL_INDEX audit (total via guest error strings)
|
|
- [x] Identify softcode post-entry → AST re-run sites
|
|
- [x] Harden with `host_ecalls`
|
|
- [x] Plan doc + issue #1791
|
|
- [ ] CI soak under artificial code-full (optional)
|
|
)
|