Repository navigation
octane: free the bus and its decode table when the machine drops - #396
Merged
Merged
Conversation
The compile workers read the page array through raw pointers and hold the bus they compile from. Jitv2 declares pages before compile_queue, so when the last reference went, the array was freed first and the workers were joined only afterwards: a use-after-free. Nothing hit it while a dropped machine's CPU leaked. The next changes let it go. Jitv2 now stops its pool in its own Drop, and a CompileQueue dropped alone does the same, through stop(). stop() skips the calling thread, since a worker can hold the last reference for a moment, and it drains the process-wide hot queue, so a dropped machine leaves no request behind that points at freed pages.
The lock registry is a static that is never emptied, and the Bridge registered its locks with probe closures holding a strong clone of the Bridge. The Bridge keeps HEART and its memory alive, and the memory's DMA view owns the physical bus, so one registry entry kept a dropped Octane's whole memory map, and a slot in the process's 64 decode tables, for the life of the process. The helpers (register_mutex_fn, register_spin, register_mutex) now downgrade the Arc they are given, and the Bridge uses them. IOC3, ISP1040 and RAD1 already held their owners weakly. The cost is that the locks monitor command still lists a dropped machine's entries, as free. The Indy-family register_locks methods are left as they are.
A GUI Stop then Start on an Octane left the old machine's physical bus behind, and a process can have only 64 decode tables: a test binary that built Octanes one after another failed at the 65th with "devmap: more than 64 tables alive". Three things held the bus. The lock registry did, which the earlier commit fixes. The Bridge and IMPACT hold HEART's DMA view, which owns the bus, and the bus holds them; and under jitv2 the compile workers held it, which the first commit fixes. Nothing broke the second cycle. The machine's drop, which already stops the CPUs and devices, now calls Physical::release_devices. release_devices points the whole decode table at the bus's own error device before it lets go of the devices, so an access that still arrives gets a bus error, not a freed device. The Octane also stops forgetting an Arc of its CPU, which kept the CPU alive under jitv2; as_device returns the CPU's own allocation, so the pool's weak handle needs no extra reference.
Owner
|
we need a rebase plz |
jamesbraid
force-pushed
the
fix-octane-devmap-leak
branch
from
November 2, 2026 10:06
4353128 to
7fe0d54
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An Octane machine keeps its bus and its device-map table alive after it's dropped, and a process only has 64 tables. Build Octanes one after another and the 65th aborts with
devmap: more than 64 tables alive. That happens on main too. A GUI Stop then Start would do the same, though I haven't run that.Three things kept the bus alive, one commit each:
Jitv2freed the pages before it joined the workers, which is a use-after-free once a CPU can actually be dropped. It now stops its workers when it drops. This commit goes first so the later ones don't expose the race.Things to look at:
locksmonitor command still lists a dropped machine's entries, as free.lockscommand.a_dropped_octane_frees_its_decode_tablebuilds and drops an Octane, with and without IMPACT, and checks nothing still holds the bus. It fails without the release, with and without jitv2.dropping_a_jitv2_joins_its_workersfails without the twoDropimpls, but it only checks that the workers are gone, not the page-array race itself. That's covered by the lib suite passing five times in a row at eight threads under jitv2, and by the display-stop and new tests passing under AddressSanitizer. The suite also passes at one thread, with and without jitv2. Not run: macOS, Windows, a real GUI Stop then Start.