scan: follow a word only if it could be a heap block's start - 16-aligned, in the user address space, not in the arena

The exit scan crashed two of the gate's scenarios (world, swim: SIGBUS and SIGSEGV in lp_mem_scan at
0x0e00000c65800000 and 0x04000004e461c000): a word of data with its high bits set was handed to
malloc_size, and a zone faulted looking it up. A candidate must now be 16-aligned, at or above 4 GB
(macOS's page zero), below 2^47, and outside the frame arena before malloc_size sees it; a block's
own words are read only once malloc_size has said it is one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 18:21:37 +03:00
parent 5aa7c03022
commit 91f91716b5
3 changed files with 47141 additions and 47087 deletions

View file

@ -1265,13 +1265,22 @@ function emit_fence_frame() -> void {
emith("no:\n") emith("no:\n")
emith(" ret i1 0\n") emith(" ret i1 0\n")
emith("}\n") emith("}\n")
emith("; a candidate is followed only if it could be a heap block's start: 16-aligned (malloc's), inside the\n")
emith("; user address space (above the first 4 GB, below 2^47 - a word with high bits is data, and some\n")
emith("; zones fault looking one up), and not in the frame arena; only then does malloc_size look at it\n")
emith("define internal void @lp_visit(i64 %w) {\n") emith("define internal void @lp_visit(i64 %w) {\n")
emith("entry:\n") emith("entry:\n")
emith(" %z = icmp eq i64 %w, 0\n") emith(" %al = and i64 %w, 15\n")
emith(" %al = and i64 %w, 7\n")
emith(" %mis = icmp ne i64 %al, 0\n") emith(" %mis = icmp ne i64 %al, 0\n")
emith(" %skip = or i1 %z, %mis\n") emith(" %low = icmp ult i64 %w, 4294967296\n")
emith(" br i1 %skip, label %out, label %size\n") emith(" %high = icmp uge i64 %w, 140737488355328\n")
emith(" %s1 = or i1 %mis, %low\n")
emith(" %s2 = or i1 %s1, %high\n")
emith(" br i1 %s2, label %out, label %arena\n")
emith("arena:\n")
emith(" %wp = inttoptr i64 %w to ptr\n")
emith(" %inar = call i1 @lp_in_arena(ptr %wp)\n")
emith(" br i1 %inar, label %out, label %size\n")
emith("size:\n") emith("size:\n")
emith(" %p = inttoptr i64 %w to ptr\n") emith(" %p = inttoptr i64 %w to ptr\n")
emith(" %sz = call i64 @malloc_size(ptr %p)\n") emith(" %sz = call i64 @malloc_size(ptr %p)\n")

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff