diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-03 00:21:52 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-03 00:21:52 +0200 |
| commit | 834b931d068a9a76e2c2a85267f24a22e93276cf (patch) | |
| tree | 55fec8d47398a51a0c40dec8fba4f9664a12233a /rsnix/src/vt.rs | |
| parent | v0.5.7: vt verify also as a boot marker (real hardware has no serial) (diff) | |
| download | rs-dos-834b931d068a9a76e2c2a85267f24a22e93276cf.tar.gz rs-dos-834b931d068a9a76e2c2a85267f24a22e93276cf.zip | |
v0.5.7: guard dumps block-header math + 16-word window; vt content verify; shadow marker
The klog -t 2 crash drops a 126-byte String (the vt verify klog line)
whose data ptr 0x40444970 points into the first-heap-block region whose
content is the firmware 0x00AAAAAA scrub — a stale pointer, not a real
allocation (back-pointer slot never written). Upgrades so the next
real-hardware crash shows the full picture:
- guard: explicit candidate block bases (b=ptr-24 / b16=ptr-32) with
their size/free fields, plus a 16-word window (mem..mem4) from
ptr-0x30 instead of the old 8 words
- vt::debug_verify: content probe of the cells grid (must be all-BLANK
at shell entry) — catches a stale in-heap cells pointer that passes
the range checks
- gop: on-screen marker for shadow carved vs NO-SHADOW fallback
(the scrub content at the crash ptr suggests the shadow was rejected
and its block stayed free + firmware-scrubbed)
Diffstat (limited to '')
| -rw-r--r-- | rsnix/src/vt.rs | 33 |
1 files changed, 30 insertions, 3 deletions
diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs index 2d8ece6..2906258 100644 --- a/rsnix/src/vt.rs +++ b/rsnix/src/vt.rs | |||
| @@ -172,13 +172,40 @@ pub fn debug_verify() { | |||
| 172 | p >= hs && p + bytes <= he | 172 | p >= hs && p + bytes <= he |
| 173 | }) | 173 | }) |
| 174 | .unwrap_or(false); | 174 | .unwrap_or(false); |
| 175 | if c.len() != total || r.len() != total || !heap_ok { | 175 | // Content check: at shell entry nothing has been printed yet, so |
| 176 | // every cell must still be BLANK (all-zero). A stale cells data | ||
| 177 | // pointer that happens to be in-heap still passes the range | ||
| 178 | // checks (len/cap look fine) but its "cells" read back as the | ||
| 179 | // underlying memory's leftover fill — the OptiPlex shows the | ||
| 180 | // firmware 0x00AAAAAA scrub there. This catches that. | ||
| 181 | let mut content_ok = true; | ||
| 182 | if heap_ok && c.len() == total && total > 0 { | ||
| 183 | let mut i = 0usize; | ||
| 184 | // Sample the first cell, the last cell, and a few interior | ||
| 185 | // cells — reading the whole grid here would be ~60 KiB of | ||
| 186 | // checks at every boot for no real benefit. | ||
| 187 | let probes = [0usize, total / 3, (2 * total) / 3, total - 1]; | ||
| 188 | for &p in probes.iter() { | ||
| 189 | if i >= probes.len() { | ||
| 190 | break; | ||
| 191 | } | ||
| 192 | i += 1; | ||
| 193 | let cell = *c.as_ptr().add(p); | ||
| 194 | let raw = cell.ch as u32 | ((cell.fg as u32) << 16) | ((cell.bg as u32) << 24); | ||
| 195 | if raw != 0 { | ||
| 196 | content_ok = false; | ||
| 197 | break; | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | if c.len() != total || r.len() != total || !heap_ok || !content_ok { | ||
| 176 | msg("verify: FAILED — cells/rendered not sane at shell entry"); | 202 | msg("verify: FAILED — cells/rendered not sane at shell entry"); |
| 177 | crate::gop::boot_marker(&alloc::format!( | 203 | crate::gop::boot_marker(&alloc::format!( |
| 178 | "vt: cells {:#x} {}/{} FAILED", | 204 | "vt: cells {:#x} {}/{} {}", |
| 179 | c.as_ptr() as usize, | 205 | c.as_ptr() as usize, |
| 180 | c.len(), | 206 | c.len(), |
| 181 | total | 207 | total, |
| 208 | if content_ok { "range" } else { "CONTENT-GARBAGE" } | ||
| 182 | )); | 209 | )); |
| 183 | } else { | 210 | } else { |
| 184 | msg("verify: ok"); | 211 | msg("verify: ok"); |
