diff --git a/tools/podium-repro/README.md b/tools/podium-repro/README.md index 0d3fd7c..6b81e73 100644 --- a/tools/podium-repro/README.md +++ b/tools/podium-repro/README.md @@ -96,6 +96,34 @@ anything before it could. The feeder is a close copy of the proven two-pod-test, but treat its first run as a shakedown: if the pods never reach `WaitingForLaunch`, the mesh/egg path is the thing to debug, not the crash. +## The PEB page-heap trick is DEAD on Windows 26200 (2026-08-14) + +The no-elevation trick this rig was built on no longer works, and it fails +**silently** — which is the dangerous part, because an uninstrumented run +looks exactly like an instrumented one right down to the clean exits. + +Measured at the create-process event on build 26200: + + dd $peb+68 L1 -> 00000000 (before) + ed $peb+68 02001000 + dd $peb+68 L1 -> 02001000 (the write lands) + g + dd $peb+68 L1 -> 00000000 (ntdll zeroed it during init) + +So `NtGlobalFlag` is re-initialised after `-xe cpr` and page heap never +turns on: no `verifier.dll`, no `Page heap: pid ...` line. It was verified +working on 26100; the machine has since moved to 26200. + +`feeder.ps1` now ABORTS when it cannot confirm page heap on every pod, +rather than running an uninstrumented test and reporting a clean result. +`-AllowNoPageHeap` runs anyway and says loudly in the log that the result +is not evidence of absence. + +**This makes gflags (and therefore an elevated shell) the only route to a +heap instrument on this machine** — see "Full page heap" below. That is no +loss: full page heap was already the discriminating test for the overrun +reading of the dumps, and light page heap could never have caught it. + ## Gotchas this rig has already paid for - **`dist\environ.ini` ships `RP412PODIUM=0`.** The podium is OFF by default, @@ -186,6 +214,15 @@ cannot press buttons (only the four analog channels), so it cannot fire a weapon or pop a chute; if deaths turn out to matter, they need a hazardous map or a different mechanism, not a fiercer script. +**2026-08-14, six pods, driven, podium ON — NO PAGE HEAP, and that is the +finding.** All six raced (scores 741..1010, so they really drove), all six +placed (`WinnersCircle: 6 placed on 8 spots`), full teardown, six clean +exits, no exception. Then the cdb logs turned out to carry no +`Page heap: pid` line at all: the PEB trick had stopped working (see above). +**Do not record this as a negative** — it is an uninstrumented run, which is +weaker even than the earlier light-page-heap ones. Its only value is as one +more sample of the crash failing to happen on its own. + ### What the two clean runs change Light page heap traps a **double free** at the second free. It does NOT trap diff --git a/tools/podium-repro/feeder.ps1 b/tools/podium-repro/feeder.ps1 index d7b615d..fd9ad9d 100644 --- a/tools/podium-repro/feeder.ps1 +++ b/tools/podium-repro/feeder.ps1 @@ -21,7 +21,11 @@ param( # cdbrun.txt enables LIGHT page heap itself via the PEB. # cdbrun-gflags.txt does not - use it when `gflags -i rpl4opt.exe +hpa` # has already been set (full page heap), so only one mechanism is in play. - [string]$CdbScript = 'cdbrun.txt' + [string]$CdbScript = 'cdbrun.txt', + # Run even when page heap could not be confirmed. Off by default: an + # uninstrumented clean run looks exactly like an instrumented one in the + # summary, and banking it as a negative is how a rig starts lying. + [switch]$AllowNoPageHeap ) $ErrorActionPreference = 'Stop' Add-Type -Path $MungaNetDll @@ -173,6 +177,35 @@ foreach ($k in $keys) { } } +# +# Stop rather than bank a negative that proves nothing. +# +# The PEB trick this rig was built on FAILS SILENTLY on Windows 26200: the +# write lands at the create-process event - read it back there and it is +# 0x02001000 - and ntdll zeroes NtGlobalFlag again during its own init, so +# every pod runs on an ordinary heap. It worked on 26100, which is what the +# README verified against. Nothing in the run's output would tell you: the +# races look identical and the pods exit clean either way. +# +$instrumented = @($keys | Where-Object { $podInfo[$_].pid }).Count +if ($instrumented -lt $PodCount) { + Log "PAGE HEAP NOT CONFIRMED on $($PodCount - $instrumented) of $PodCount pods" + if (-not $AllowNoPageHeap) { + Log 'ABORTING - a clean result without the heap instrument proves nothing.' + Log 'Full page heap needs an ELEVATED shell:' + Log " & 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\gflags.exe' -i rpl4opt.exe +hpa" + Log " ...feeder.ps1 -PodCount 4 -CdbScript cdbrun-gflags.txt" + Log " & '...\gflags.exe' -i rpl4opt.exe -hpa # always put it back" + Log 'Or pass -AllowNoPageHeap to run uninstrumented on purpose.' + Get-Process rpl4opt -ErrorAction SilentlyContinue | Stop-Process -Force -Confirm:$false -ErrorAction SilentlyContinue + Get-Process cdb -ErrorAction SilentlyContinue | Stop-Process -Force -Confirm:$false -ErrorAction SilentlyContinue + exit 1 + } + Log 'CONTINUING UNINSTRUMENTED (-AllowNoPageHeap) - this run can only catch' + Log 'a crash that faults on its own, as the fatal night did. A clean result' + Log 'is NOT evidence of absence and must not be recorded as one.' +} + #----------------------------------------------------------------- # Break detection: cdb writes the marker the instant it traps, well # before a race would time out.