A VS chunk too large for a pool header is big pool too - #181
Merged
Merged
Conversation
The previous fix asked the page range descriptor which allocations carry no `_POOL_HEADER`, and that is only where most of them are. `nt` also puts them inside VS subsegments, where the descriptor says `0x0f` and the chunk chain runs straight through them -- so `0xffffac09da29f000` on a live 26100.33438 kernel is an `MiRr` allocation of `0xe1c0` bytes to `!pool` and was 57,792 untagged bytes to the walk. The same length; only the name lost. The size answers it, and structurally rather than by measurement. `_POOL_HEADER.BlockSize` is **eight bits** of sixteen-byte units (`dt nt!_POOL_HEADER`, x64 26100.33438), so `0xff * 16` -- 4080 bytes of chunk, its own header included -- is the most it can describe: 4080 of payload plus the header is exactly a page, and one byte more has nowhere to record its own length. That is *why* such an allocation is in `nt!PoolBigPageTable`, and it is what the table says back: every one of the 7,639 live entries on that guest recorded `NumberOfBytes` of `0x1000` or more, and none fewer. A chunk past the limit is matched against the entries discovery resolved inside its region, **by containment and length** rather than by arithmetic on the chunk header. The table's `Va` is where the allocation begins and `NumberOfBytes` is how long it is; taking both as given costs nothing and assumes nothing about what sits between the chunk header and the data -- which on that guest is 0x10 that is measured and not yet explained, and which fitting the tag to would have been a guess. Three rules, each mutation-verified by breaking it: the limit itself, the containment match, and the length check -- the last of which the first draft did not pin at all, its fixture's entry fitting either way. windbg-mcp FOLLOWUPS.md item 99. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUhLUt9rB6zd42Y25btB3h
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 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. Comment |
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.
#180 asked the page range descriptor which
allocations carry no
_POOL_HEADER, and that is only where most of them are.ntalso puts theminside VS subsegments, where the descriptor says
0x0fand the chunk chain runs straight throughthem. Measured on a live 26100.33438 kernel:
!pool0xffffac09da29f000MiRr,0xe1c0...., 57,792 bytes0xe1c0is 57,792 — the walk had the block, at the right address and the right length, and hadlost only its name.
The size answers it, structurally
_POOL_HEADER.BlockSizeis eight bits, in sixteen-byte units:So
0xff * 16— 4080 bytes of chunk, its own header included — is the most it can describe: 4080of payload plus the header is exactly a page, and one byte more has nowhere to record its own
length. That is why such an allocation is in
nt!PoolBigPageTable, rather than a fact measuredabout one kernel — and the table says it back: every one of the 7,639 live entries on that guest
recorded
NumberOfBytesof0x1000or more, and none fewer.Which makes the question answerable from the chunk's size rather than from which allocator produced
it, and that is the point: the two are indistinguishable by their page range descriptors.
Matched by containment and length
A chunk past the limit is matched against the entries discovery resolved inside its region — the
table's
Vais where the allocation begins andNumberOfBytesis how long it is — rather than byarithmetic on the chunk header. Taking both as given costs nothing and assumes nothing about what
sits between the chunk header and the data, which on that guest is 0x10 that is measured and not
yet explained. Fitting the tag to that would have been a guess; three facts now have to agree or
nothing is claimed.
Measured
After, on the same guest:
!pool0xffffac09da29f000MiRr,0xe1c0MiRr, 57,792 bytes, header at the allocationCensus, against the run two hours earlier (the pool itself grew ~5%, 739,888 → 780,724 chunks
walked, so read the byte totals rather than the counts): the
....bulk 51,216,352 →42,785,104 bytes,
MiRrarriving at 386 allocations / 1.8 MB andMmRegoing 3.8 → 7.1 MB.Diagnostics fell 224 → 178.
Tests
Two new unit tests, each mutation-verified by breaking the rule it pins — the limit itself, the
containment match, and the length check. The third mutation passed against the first draft,
whose fixture entry fit inside its chunk either way, so it has a case of its own now.
What is left, and what this run ruled out
The residue is one thing and no longer mixed in with the tags: 1,047 chunks under the bogus tag
0x838bffff— the top half of a kernel pointer — all of them under the limit, so they keep aheader the walk places 0x10 early. Confirmed from the bytes rather than from
!pool's heuristic:a genuine
_POOL_HEADERsits at0xffff8b836d46f000withBlockSize0x21and the tagMmLd,matching
!pool's0x210, while the walk read a pointer 0x10 before it.The chunk area's start is ruled out as the cause, which was the leading hypothesis:
sizeof(_HEAP_VS_SUBSEGMENT)is0x28, the first chunk header is at+0x30where this cratecomputes it, and a real
MiSe_POOL_HEADERsits at+0x40. So the chain begins correctly anddrifts later. windbg-mcp
FOLLOWUPS.mditems 99 and 100.🤖 Generated with Claude Code
https://claude.ai/code/session_01MUhLUt9rB6zd42Y25btB3h