From f07f801944e69f7917cf229bb4bf650e178f7d18 Mon Sep 17 00:00:00 2001 From: Alexandros Mandravillis Date: Sun, 30 Aug 2026 17:26:38 +0300 Subject: [PATCH] =?UTF-8?q?runtime:=20validate=20stale-static=20ranges=20i?= =?UTF-8?q?n=20full=20=E2=80=94=20drop=20the=20exec=5Fpc=20clip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dirty_ram_text_native_ok_ranges_from clipped validation to [exec_pc, end), assuming a continuation never fetches bytes behind its resume PC. False for any entry with a backward edge: a post-call continuation re-enters mid-function and loops back into the clipped region, executing stale static code. CMR2's overlay engine carries a second copy of the vertex-transform kernel at 0x80016B34 that forked exactly this way (r16 +0xA0 static vs +0x100 live, the same 0x60 delta as the first locus) after the inter-piece transfer guards closed the fallthrough edge. Validation now covers every emitted range of the entry regardless of resume PC. A genuinely patched prologue blocks the whole entry to the dirty-RAM interpreter -- correct, merely slower. --- runtime/src/memory.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/runtime/src/memory.c b/runtime/src/memory.c index 4d82bbdc..6a438311 100644 --- a/runtime/src/memory.c +++ b/runtime/src/memory.c @@ -482,15 +482,19 @@ int dirty_ram_text_native_ok(uint32_t phys) { * a mismatch never poisons an unrelated 4 KB page forever: every decision is * made from the live bytes the native body will actually execute. * - * exec_pc is the dispatch/resume address. Ranges that end at or before that PC - * are skipped, and a range that straddles it is clipped to [exec_pc, end). A - * runtime patch of a function prologue must not block a compiled continuation - * that never fetches the patched bytes. */ + * exec_pc is the dispatch/resume address, kept for observability and future + * use; ranges are validated IN FULL regardless of it. The former + * [exec_pc, end) clip assumed a continuation "never fetches the patched + * bytes" behind its resume PC — false for any entry with a backward edge: + * a post-call continuation re-enters mid-function and loops back into the + * clipped-away region, executing stale static code (CMR2 overlay + * corruption, second locus 0x80016B34). A genuinely patched prologue now + * blocks the whole entry to the interpreter — correct, merely slower. */ int dirty_ram_text_native_ok_ranges_from(const uint32_t *lo_len_pairs, uint32_t count, uint32_t exec_pc) { if (!text_ref_image || !lo_len_pairs || count == 0) return 0; - uint32_t at = exec_pc & 0x1FFFFFFFu; + (void)exec_pc; int any = 0; for (uint32_t i = 0; i < count; i++) { uint32_t phys = lo_len_pairs[i * 2u] & 0x1FFFFFFFu; @@ -500,11 +504,6 @@ int dirty_ram_text_native_ok_ranges_from(const uint32_t *lo_len_pairs, g_text_native_blocked++; return 0; } - if (phys + len <= at) continue; - if (phys < at) { - len -= at - phys; - phys = at; - } any = 1; /* Page-clean fast path: every page this range touches is neither * guard-modified nor runtime-dirty, so its bytes still equal the