Repository navigation
fix(runtime): drop an unmapped page's record as the runtime's own work - #245
Merged
Merged
Conversation
The DLL maps a page through its copy of the runtime, which records it, and unmaps it, which drops the page's record. Built alone and with the operators new and delete that go through the copy, it lets a test check that the copy drops the record without waiting on its own lock. Refs #243
On Windows, __ct_munmap dropped the freed record of a page with the allocation table's lock held, outside a runtime scope. Erasing the record frees a node of the table, and that release may reach the runtime's entry points through the copies of the library's inline functions that an instrumented module compiles (#197). The entry point then took the table's lock again from the thread that held it, and the unmap never returned. __ct_munmap now runs in a runtime scope, as the other entry points that release a record under that lock do: the entry point that the node's release reaches sees the runtime's own work and passes it on without taking the lock. Refs #243
A child process loads the allocating test DLL, which maps a page through its copy of the runtime and unmaps it, and exits with 0 once the unmap returned. A watchdog, running before the DLL loads, ends the child with 7 after 20 seconds. Refs #243
The same child process loads the DLL whose operators new and delete go through its copy of the runtime. Dropping the page's record frees a node of the copy's table with the table's lock held, and the node comes back to the copy's entry points through the DLL's operator delete, as through an instrumented DLL. The test fails with 7 when the unmap waits on that lock. Refs #243
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.
Refs #243
Summary
On Windows,
__ct_munmapdropped the freed record of a page with the allocation table's lock (ct_alloc_lock) held, outside a runtime scope. Erasing the record frees a node of the table. When that release reaches the runtime's entry points, as through the copies of the library's inline functions that an instrumented module compiles (#197), the entry point opens a scope and takesct_alloc_lockagain, from the thread that holds it: SRW locks are not recursive, and the unmap never returns.__ct_munmapnow runs in a runtime scope (CtRuntimeScope), asct_release_tracked_pointerandct_autofree_implalready do around the same releases. The entry point that the node's release reaches sees the runtime's own work and passes it on without taking the lock. The diff to the runtime is the scope and its comment, insrc/runtime/windows/ct_runtime_alloc.cpp.The POSIX runtime does not take this path: its table marks a dropped record as a tombstone in place and frees nothing.
Evidence
The tests ran in the four Windows jobs of
LLVM versions(LLVM 19, 20, 22, 23), onmainand on this branch.probe/243-red, 5cc839emain(c11f8a5) with the test DLL's new function and the two tests below, without the fixADllWhoseOperatorDeleteFreesThroughItsCopyUnmapsAPagefails with exit status 7, after 20.01 to 20.02 seconds; the control passes.LLVM versionspass, and so doBuild,clang-format,license-complianceand the commit conventions.#243's first probe (
probe/munmap-forget-lock, 381e187, run 41341737799) gave the same result on a head of #242.Tests
The allocating test DLL gains
ct_allocating_library_map_and_unmap, which maps a page through its copy of the runtime (__ct_mmap), which records it, and unmaps it (__ct_munmap), which drops the page's record. Two death tests load a DLL in a child process that starts its watchdog first (startWatchdog(), exit status 7 after 20 seconds) and exit with 0 once the unmap returned:ADllThatMapsAPageThroughItsCopyUnmapsItct_allocating_library, with the C++ library's operatorsADllWhoseOperatorDeleteFreesThroughItsCopyUnmapsAPagect_new_delete_through_runtime_library, whose operators new and delete go through the copy, as a model of #197Limits