Repository navigation
Description
Summary
On Windows, __ct_munmap drops the freed record of a page with the allocation table's lock (ct_alloc_lock) held, outside a runtime scope (CtRuntimeScope). Erasing the record frees a node of the table. When that release reaches the runtime's entry points, as it may through the copies of the library's inline functions that an instrumented module compiles (#197), the entry point takes the same lock again, from the thread that holds it: the call never returns.
The path
In src/runtime/windows/ct_runtime_alloc.cpp, on main and on #242, which does not change these functions:
__ct_munmaptakes the lock without a scope and marks the page's record as freed (ct_remove_for_release), then releases the lock.- When the page was recorded, it calls
ct_forget_returned_block, which takes the lock again, still without a scope, and callsct_table_forget_freed. ct_table_forget_freederases the record:std::unordered_map::erasefrees the record's node withoperator delete.- Through a copy of the library's deallocation that calls
__ct_freeor__ct_delete, the release entersct_release_tracked_pointeroutside a scope: it opens one and takesct_alloc_lock, which its own thread holds. SRW locks are not recursive.
The other releases of a record under that lock run in a scope: ct_release_tracked_pointer, ct_autofree_impl (which calls ct_forget_returned_block from its scope), and the quarantine's pushes. #242 puts the table's build in a scope of its own, which covers the lookups of __ct_check_bounds and __ct_realloc that take the lock without a scope; those lookups free nothing.
Reproduction
probe/munmap-forget-lock (381e187), on ad71cea, a head of #242, which has a test DLL whose operators new and delete go through its copy of the runtime (__ct_malloc, __ct_free), as a model of #197. The DLL maps a page through its copy (__ct_mmap) and unmaps it (__ct_munmap), in a child process that a watchdog ends with 7 after 20 seconds. Run 41341737799, Windows jobs of LLVM versions:
| Job | ProbeADllThatAllocatesThroughItsCopyUnmapsAPage (control: the library's own operators) |
ProbeADllWhoseOperatorDeleteFreesThroughItsCopyUnmapsAPage |
|---|---|---|
| Windows / LLVM 19 | passes, 0.01 s | fails: exit status 7, after 20.02 s |
| Windows / LLVM 20 | passes, 0.01 s | fails: exit status 7, after 20.02 s |
| Windows / LLVM 22 | passes, 0.01 s | fails: exit status 7, after 20.02 s |
| Windows / LLVM 23 | passes, 0.01 s | fails: exit status 7, after 20.02 s |
The 236 other tests pass in each job.
Not established
- Whether an instrumented program reaches this path: it needs the linker to keep, for the runtime, the instrumented copy of the library's deallocation, and a program that unmaps a mapping the runtime recorded. The probe forces both.
- The POSIX runtime was not checked.
Activity