Skip to content

pool: every VS refusal on a live walk is the subsegment bound, which makes the bound the suspect #103

Description

@glslang

First live 26100 walk since #102 split the VS plausibility predicate and made the refusal count a count. Both changes paid off immediately, and what they show points somewhere I dismissed while writing them.

The measurement

pool_census over KDNET, 408,013 chunks walked (241,970 allocated) in ~19s, partial, 3,569 diagnostics in 14 categories. Every VS diagnostic:

   542x  # VS chunk headers refused, resynchronising from #
   542x  refusing VS chunk at # the chunk runs past the end of its subsegment (size # previous size # encoded #
    68x  # VS chunks disagreed with the size of the chunk before them, from #
    68x  VS chunk at # records a previous size of # where the chunk before it measured #

and the walk's own total, which no diagnostic can carry:

"refused_chunks": 106516

Two things worth separating

1. One predicate accounts for every refusal. decode_vs_chunk has three: the size word decoding to zero, a chunk smaller than its own headers, and a chunk running past its subsegment. On a walk with 106,516 refusals, the first two fired zero times. That is not what corruption looks like — a header rewritten under us, or a wrongly keyed word, would spray across all three. One predicate at 100% is the shape of a predicate that is wrong.

The obvious candidate is the bound itself, which is region.address + region.size. #93 raised exactly this ("worth checking while in here: whether region.size is the right bound") and I dismissed it in #102 on the grounds that the test is > rather than >=, so a chunk ending exactly at the boundary is accepted. That answers the exact-boundary case and says nothing about a bound that is systematically too small — which is what this looks like. #92's discover_segment_context advances a VS region past _HEAP_VS_SUBSEGMENT and shrinks its size to match; if that shrink is short, or if the walk starts at a base that is not the subsegment's, every chunk near the end of every extent overruns.

2. 106,516 is refusals, not lost chunks. A refusal advances 16 bytes and tries again, so one lost sync inside an extent bills one refusal per 16 bytes until it resynchronises: 106,516 refusals across 542 extents is ~196 per extent, ~3.1 KB of brute-force scanning each. The count is still the right number to carry — it is what the walk actually did — but it sizes the disruption, not a population of corrupt chunks, and anything reading it as the latter will overstate the damage by orders of magnitude.

What would settle it

The encoded word travels with the refusal now, precisely so a refused header can be re-decoded by hand. Take one refused chunk from a sample, decode its Sizes against the subsegment's own _HEAP_VS_SUBSEGMENT extent read directly from the target, and compare that extent with the region.size the walker was using. If they differ, the bound is the bug and 106,516 refusals are an artefact of it.

The chain check is the cross-check that makes this readable: only 68 extents show a chunk disagreeing with its predecessor's size. If the chunks themselves were corrupt at anything like the rate the refusals suggest, that number would not be 68.

Reproduce

cargo test --test mcp_smoke -- --ignored --nocapture --test-threads=1 live_kernel

with WINDBG_MCP_SMOKE_KERNEL sourced from the configured profile, then pool_diagnostics with filter VS chunk for verbatim samples carrying real numbers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions