You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The other half of Enforcer's bug class (see #71 for NULL/low memory): a wild pointer into an address that isn't backed by guest RAM at all.
The evidence
LawBreaker (from the Enforcer package, see #70) does this deliberately:
207c aaaa 4444 movea.l #$AAAA4444,a4 ; a wild address, by design
2014 move.l (a4),d0 ; read from it
volamos reports nothing, even with --sanitize. The reason is in AddressSpace's documented contract in memory.rs, which is deliberately total — no Result, no panics:
Out-of-range behavior is deliberately simple and total: reads past the end of the backing store return 0, and writes past the end of the backing store are silently ignored.
That total contract is the right call for the CPU hot path and should stay. But it means a wild read quietly yields 0 and a wild write quietly evaporates, so a guest that dereferences garbage gets plausible-looking zeroes instead of a diagnosis — and on real hardware this is exactly what Enforcer prints a report for.
Note this is distinct from the existing StopReason::PcOutOfBounds, which catches the program counter leaving guest memory (implemented already). This is about data accesses.
Proposal
Report an out-of-range guest data access as a sanitizer violation, while keeping the total read-as-0/drop-the-write semantics exactly as they are — a detector, not an enforcer, consistent with the rest of --sanitize.
Design notes:
The shadow map is sized to guest RAM, so an out-of-range address has no shadow byte to poison. The check therefore belongs in the access paths' existing out-of-range branches (the None arms of FlatMemory's read_u16/read_u32/write_* fast paths, and the get/get_mut misses in the byte paths) rather than in the shadow map's state lookup. ShadowMap::state already returns Valid for out-of-range addresses precisely because the total contract handled them; that becomes the thing to change.
Worth distinguishing "just past the end of RAM" (a plausible off-by-one on a large buffer) from "nowhere near RAM" ($AAAA4444, a garbage pointer) in the message, since they suggest very different bugs.
Scope
Report out-of-range guest data reads and writes as violations with their own PoisonReason/kind, preserving the total read-0/drop-write behaviour.
Handle the straddling case, reporting once per access.
Fixture with a deliberate wild-pointer read and write.
False-positive sweep against PhxAss, pLhA and SAS/C sc — all must stay clean. Note there is real risk here: any program that probes memory by reading it (autoconfig-style scans, "how much RAM is there" loops) would legitimately trip this, which may be an argument for it being opt-in.
The other half of Enforcer's bug class (see #71 for NULL/low memory): a wild pointer into an address that isn't backed by guest RAM at all.
The evidence
LawBreaker (from the Enforcer package, see #70) does this deliberately:
volamos reports nothing, even with
--sanitize. The reason is inAddressSpace's documented contract inmemory.rs, which is deliberately total — noResult, no panics:That total contract is the right call for the CPU hot path and should stay. But it means a wild read quietly yields
0and a wild write quietly evaporates, so a guest that dereferences garbage gets plausible-looking zeroes instead of a diagnosis — and on real hardware this is exactly what Enforcer prints a report for.Note this is distinct from the existing
StopReason::PcOutOfBounds, which catches the program counter leaving guest memory (implemented already). This is about data accesses.Proposal
Report an out-of-range guest data access as a sanitizer violation, while keeping the total read-as-
0/drop-the-write semantics exactly as they are — a detector, not an enforcer, consistent with the rest of--sanitize.Design notes:
Nonearms ofFlatMemory'sread_u16/read_u32/write_*fast paths, and theget/get_mutmisses in the byte paths) rather than in the shadow map's state lookup.ShadowMap::statealready returnsValidfor out-of-range addresses precisely because the total contract handled them; that becomes the thing to change.straddling_out_of_range_write_partially_applies). Straddling should report once, consistent with the report-once-per-access rule from Extend --sanitize to stack bugs: below-SP accesses and return-address corruption (issue #65) #67.$AAAA4444, a garbage pointer) in the message, since they suggest very different bugs.Scope
PoisonReason/kind, preserving the total read-0/drop-write behaviour.peek_u16/peek_u32(from Extend --sanitize to stack bugs: below-SP accesses and return-address corruption (issue #65) #67) exempt, as they already are for every other check.sc— all must stay clean. Note there is real risk here: any program that probes memory by reading it (autoconfig-style scans, "how much RAM is there" loops) would legitimately trip this, which may be an argument for it being opt-in.$AAAA4444read.Related: #65, #70, #71, #73.