Skip to content

Enforcer-style register snapshots in sanitizer violation reports #73

Description

@sidick

Sanitizer violation reports currently name the faulting PC and address:

invalid 1-byte write at 0x00002ee0 (heap redzone) from PC 0x00002af6

Enforcer's reports also dump all sixteen CPU registers at the moment of the fault, and that is not decoration — it is often how you identify the culprit, because the registers tell you where the bad pointer came from when the faulting instruction itself only says move.l (a4),d0.

The evidence that this matters

LawBreaker (from the Enforcer package, see #70) goes out of its way to make the point. Before committing any violation it loads every single register with a recognisable pattern:

207c aaaa 0000   movea.l #$AAAA0000,a0
227c aaaa 1111   movea.l #$AAAA1111,a1
247c aaaa 2222   movea.l #$AAAA2222,a2
...
203c dddd 0000   move.l  #$DDDD0000,d0
223c dddd 1111   move.l  #$DDDD1111,d1
...
2e3c dddd 7777   move.l  #$DDDD7777,d7

$AAAAnnnn in the address registers, $DDDDnnnn in the data registers, each numbered by register. That pattern exists solely so that a human reading Enforcer's register dump can verify at a glance which register the report attributed the fault to. A test program designed around the register dump is decent evidence the dump is the valuable part of the report.

Proposal

Capture a register snapshot when a violation is recorded and include it in the report.

Design notes:

  • ShadowMap::record currently has access to the PC only, published per-instruction by the run loop (backend.rs, set_current_pc). The registers live on the Cpu, which the shadow map cannot see. The cheapest approach consistent with the existing design is for the run loop to publish the registers alongside the PC, the same way it already publishes the PC — but note that would mean copying 16 longwords per instruction, on the hot path, to be used only in the rare violation case. Measure before committing to that; a cheaper option is to have the run loop notice that the violation count changed after an instruction and only then attach a snapshot.
  • Violations are deduplicated by (pc, addr, kind) with a hit count (Add --sanitize, a valgrind/ASan-style memory sanitizer (issue #65) #66) and rolled up per PC (proposed in Optional --sanitize-uninit: uninitialized-read detection as an opt-in extra (issue #65 increment 3) #68). A snapshot should therefore be attached to the first occurrence, with the report making clear it is from that occurrence and not the most recent.
  • Keep the one-line summary form for scanning, with registers as an indented block underneath, so a report with many violations stays readable. Enforcer's format is worth looking at directly for the layout.

Possible follow-up worth noting here

volamos's loader now parses (and skips) HUNK_DEBUG blocks as of #70. LawBreaker's own debug hunks contain HEADDBGV01 source-line information referencing LawBreaker.asm, and the binary embeds a LINE marker. That raises a genuinely interesting possibility: translating a faulting PC back to a hunk:offset, or even a source file and line number, when the binary was built with debug info. That is now filed as #74, with the LINE debug-hunk format fully decoded and verified against LawBreaker's instruction boundaries.

Scope

  • Capture a register snapshot (D0-D7, A0-A7, PC, and SR if cheap) on the first occurrence of each violation.
  • Decide the capture strategy and measure its cost on the interpreter hot path before adopting a per-instruction copy.
  • Render registers as an indented block under the existing one-line summary.
  • Verify against LawBreaker: the $AAAAnnnn/$DDDDnnnn patterns should be plainly visible in the dump, which makes it self-verifying.

Related: #65, #68 (per-PC rollup, which interacts with where a snapshot attaches), #70, #71, #72.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions