Repository navigation
Conversation
A block that FreeVec poisoned keeps that marking when the heap hands the same addresses back out, so the allocating handler's own header write or zeroing, and then every write the guest makes, are all reported as use-after-free. Writing over the block does not clear it either: check_write_byte heals Uninit bytes but records and keeps Unaddressable ones. Clear the shadow where the block is handed out, before the handler writes anything into it. AddressSpace::clear_fresh_block does it, so the allocators that only hold a &mut dyn AddressSpace can call it too. BenchWork's "backdrop lha-pack" under --sanitize went from 6 minutes and 691416 violations to 10 seconds and none. Fixes sidick#95 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6738293 to
1159eed
Compare
|
Closing in favour of #97, which contains this commit unchanged plus the rest of the fix. Thanks for the diagnosis here -- it was right, and the Two reasons for continuing it there rather than merging this first: The clear reached 8 of ~48 allocation sites, and the gap is reachable. The note about the other modules ("nothing observed reaches them") turns out not to hold. A guest that frees a block bigger than the 64 KiB quarantine budget -- released immediately rather than held back -- and then opens a file reproduces #95's symptom against the The wall-clock half of #95 has a different cause than the issue guessed. Not the dedup hash: that measures as noise (I implemented the pre-screen and got 1.31s -> 1.32s). Also fixed there: |
Fixes #95.
A block that
FreeVecpoisoned keeps itsPoisonReason::Freedmarking when the heap hands the same addresses back out. Two kinds of write then get reported as use-after-free: the allocating handler's own header write or zeroing, and afterwards every write the guest makes into its fresh buffer. Writing over the block does not clear the marking --check_write_byte()healsUninitbytes but records and keepsUnaddressableones -- so several handlers' "these writes heal the shadow bytes, so poison afterwards" comments hold only for blocks that were never freed.Changes
AddressSpace::clear_fresh_block(), a no-op by default and implemented byFlatMemoryto clear the shadow over the block. A trait method rather than aHandlerContexthelper so thatcreate_io_request(), which only holds a&mut dyn AddressSpace, can call it too.AllocMem,AllocVec,AllocPooled,CreatePool,AllocateTagItems,AllocDosObject,CreateMsgPort,CreateIORequest.alloc_vec_handler()additionally marks[user_ptr, user_ptr + user_rounded)uninit (valid withMEMF_CLEAR).user_ptris the address that excludes theFreeVecheader, which is what the existingfalseargument was protecting; as a side effectAllocVecallocations now carry uninitialized state for--sanitize-uninit, where before they were marked as nothing at all.a_recycled_alloc_vec_block_is_no_longer_a_freed_block: with the quarantine budget set to 0 so the block really is recycled, allocate, free, allocate the same size, write one byte, and expect no violations. Verified that it fails with the fix removed.Other allocation sites (
dosargs.rs,dosseg.rs,intuition.rs,dosdevproc.rs) write into fresh blocks without any shadow marking at all; they would want the same one-line call if their blocks can ever be recycled, but I have left them alone since nothing observed reaches them.Verification
Both cases from #95, BenchWork under
--sanitize --cpu 68020:backdrop lha-packlha-pack zlib-inflateAllocVecheader pair)Benchmark checksums are unchanged throughout (
4e91d2c2,3f1b1c70,72e7a604), andbackdrop lha-pack zlib-deflate png-encodeis also clean.cargo test --release: 860 passed, 1 failed. The failure isexectask::tests::pending_host_break_folds_into_wait, which fails the same way on an unpatched tree and picks a different member of that family from run to run, so it looks like a race on the pending-break state rather than anything here.🤖 Generated with Claude Code