Skip to content

user_heap_smoke could not create a Segment Heap - #186

Merged
glslang merged 1 commit into
mainfrom
segment-heap-via-rtlcreateheap
Sep 24, 2026
Merged

glslang merged 1 commit into
mainfrom
segment-heap-via-rtlcreateheap

Conversation

@glslang

@glslang glslang commented Sep 24, 2026

Copy link
Copy Markdown
Owner

HEAP_CREATE_SEGMENT_HEAP is documented as a HeapCreate option and is not one:

KERNELBASE!HeapCreate+0xc:
  and ecx,40005h        ; HEAP_CREATE_ENABLE_EXECUTE | HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE

0x100 is dropped before RtlCreateHeap is reached, so this example built a classic NT heap and then failed two hundred lines later saying the created heap was not among the roots — which reads as a defect in root enumeration, and is not one. That misreading is already in CHANGELOG.md, where it is blamed on ARM64 and on the debug heap.

Measured

x64 26200, 2026-09-24, one process, ntdll!RtlpHpHeapFeatures = 0 throughout:

  no flag, growable         -> NT
  SEGMENT_HEAP, growable    -> NT        <- through HeapCreate
  SEGMENT_HEAP, initial 4K  -> NT
  SEGMENT_HEAP, fixed 1M    -> NT
  RtlCreateHeap, no flag    -> NT
  RtlCreateHeap + 0x100     -> segment   <- same flags HeapCreate composes, plus the one it drops

The wrapper is the whole of the difference. The three HeapCreate shapes are there because RtlpCreateHeap's flag branch does test HEAP_GROWABLE and the parameters block, so "wrong shape of request" had to be ruled out before blaming the mask.

The per-process switch is a separate thing, and worth knowing because it is what makes some processes walkable at all: ntdll!RtlpHpHeapFeatures bit 0 governs the heaps an image gets without asking. It is 1 in sihost, whose four heaps are Segment, and 0 in cmd.exe. RtlpCreateHeap tests the caller's flag at +0x14b and that global at +0x155, and both feed one decision register read at +0x142.

The fix

create_segment_heap() calls RtlCreateHeap with the flags HeapCreate composes for (_, 0, 0) — HEAP_GROWABLE, plus heap class 1 from its own bts ecx,0Ch — plus 0x100, and reads the signature back at creation. A build that stops honouring the flag now fails at the call, naming what it got, instead of surfacing as a missing root; the assertion also tells the reader which two routines decide it.

_SEGMENT_HEAP.Signature and _HEAP.SegmentSignature are both at +0x10; _HEAP.Signature (0xeeffeeff) is at +0x98. The constants here are commented with that, because I conflated the two while diagnosing this.

Verified

Green on this host again, both arms:

3 roots, 84 chunks, 35 busy blocks agreed, coverage Complete, layout fnv1a64:35f4842b07943b90 (affinity_slot_vs_offset)

435 tests pass, cargo fmt --all -- --check clean, and cargo clippy --all-targets -- -D warnings reports the same 12 pre-existing findings as main on this toolchain and none from this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MUhLUt9rB6zd42Y25btB3h

`HEAP_CREATE_SEGMENT_HEAP` is documented as a `HeapCreate` option and is not
one. `KERNELBASE!HeapCreate` opens with `and ecx,40005h` —
`HEAP_CREATE_ENABLE_EXECUTE | HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE` —
so 0x100 is dropped before `RtlCreateHeap` is reached, and the example built
a classic NT heap instead.

It then failed two hundred lines later saying the created heap was not among
the roots, which reads as a defect in root enumeration and is not one. That
misreading is already in this changelog, blaming ARM64 and the debug heap.

Measured on x64 26200, 2026-09-24: through `HeapCreate`, growable, with an
initial size and with a fixed maximum all returned an NT heap; the direct
`RtlCreateHeap` call returned a Segment Heap in the same process moments
later, with `ntdll!RtlpHpHeapFeatures` at 0 throughout. So the wrapper is the
whole of the difference, and the per-process feature bit — 1 in `sihost`,
whose four heaps are Segment, 0 in `cmd.exe` — governs only the heaps an
image gets without asking.

The example now calls `RtlCreateHeap` with the flags `HeapCreate` composes
for `(_, 0, 0)` plus the one it will not pass on, and reads the heap's
signature back at creation so a build that stops honouring it fails there
rather than as a missing root. Runs green on this host again: 3 roots, 84
chunks, 35 busy blocks agreed, coverage Complete, on the live target and on
the full-memory dump.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhLUt9rB6zd42Y25btB3h
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Sep 24, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 1f255880-3d5a-4f90-b3cf-314165a0da86


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@glslang
glslang merged commit 86d1def into main Sep 24, 2026
7 checks passed
@glslang
glslang deleted the segment-heap-via-rtlcreateheap branch September 24, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant