Repository navigation
Windows: since #6678, GTA V crashes in the open world: resumed write faults clobber the red zone again (#361) #9136
Description
What
Since #6678, GTA V (PPSA04264) crashes in the open world on Windows, in every run. The cause is the red zone damage of #361: a fault that AnyPS5 resumes in guest code can overwrite [rsp-0x80, rsp-0x10). Deferred settles return small imports to the write watch, and the guest's own stores into that memory then fault and get resumed. #7473 adds more resumed faults of the same kind on resident runs.
FATAL: unhandled exception 0xc0000005 on thread '[RAGE] HDD Streamer'
rip gtav.exe+0x28baa50 (eboot 0x28aaa50)
read of 0x000000000000774f
At eboot 0x28aaa50, the game's zlib inflate_fast reloads two pointers from its red zone (mov -0x78(%rsp),%r9, mov -0x70(%rsp),%rbx) and then reads through rdx, which holds a small garbage value. The same code crashed this way before #637/#5596.
Measured
Every fault the vectored handler resumes was logged, with its rip, data address and VirtualQuery type (a diagnostic patch in ReportCrash, not proposed). The game resumes a story save in Los Santos. Windows 11 26200, RTX 4090 (NVIDIA 616.92), i9-13900K, WinLibs GCC 15.2.0 r7, Release, write watch on.
| tree | resumed faults | in zlib (0x28aa…/0x28ab…) |
open world |
|---|---|---|---|
main 47a266c (10-17) |
0 in 240 s | 0 | no crash |
| a84027d (merge before #6678) | 0 | 0 | no crash |
| 755fecb (#6678 merge) | 134 | — | — |
main f6ce8e3 (10-20) |
400+ | ~60, all MEM_MAPPED 4 KiB subpages of 16 KiB views |
crash, 7 of 7 runs |
main 275e896 (10-21) |
400+ | also 75 on MEM_PRIVATE 512 KiB resident runs (eboot 0x28abdd4) |
crash |
main 7280a17 + APS5_NO_DEFERRED_SETTLES=1 |
133 (85 MEM_MAPPED, 44 MEM_PRIVATE) |
0 | no crash in 150 s |
The first two rows and the #6678 row come from a first-parent git bisect run whose step counts the resumed faults over 60 s of Story: "bad" means at least one. The plain crash bisect was too slow and too random to use. The merge before #6678 had 0 faults; #6678 had 134.
- perf(agc): settle Windows imports under 1 MiB without waiting #6678.
rewatchSettledImportsputs settled imports under 1 MiB back in the write watch. GTA's buffers are resident direct memory when they are imported, andMEM_MAPPED16 KiB views when the stores fault. So they become shared views after they return to the watch, and from then on each guest store into them is armed and resumed.- I tried skipping the rewatch unless the whole range is
MEM_PRIVATEat rewatch time. That changed nothing: the memory is still private at that point. - A fresh shared view is imported watched from the start, never compared. So the import path alone does not keep shared views fault-free.
- I tried skipping the rewatch unless the whole range is
- perf(libc): skip write-watch queries for proven clean resident ranges #7473.
resumeResidentWritesarms resident runs that have been proven clean. The 44 to 89MEM_PRIVATEfaults per run come from it, and 16 to 75 of them land in zlib. - Remaining. 85
MEM_MAPPEDfaults (eboot0x162b9e4,0x28f413a) remain with deferred settles off.main47a266c had none, so at least one more merge between 10-17 and 10-21 arms memory this title writes. I have not bisected that one yet.
Question
Every new arming of guest-written memory brings #361 back on Windows. A few options, from narrowest to most general:
- keep memory that may become a shared view out of the deferred-settle rewatch;
- never arm resident runs (perf(libc): skip write-watch queries for proven clean resident ranges #7473);
- track shared views by comparison instead of arming them on Windows, as Windows: resuming a write fault overwrites the guest red zone when AVX state is live #361 suggested.
@EagleBearerYS @Dripl1x, since this is your code: which direction would you take? I can test any branch on GTA within the hour: the fault log patch gives a deterministic count per run.
AI-assisted: yes (Claude Code). Measured on the machine above.
Activity
EagleBearerYS commentedon Oct 21, 2026
@tibzejoker, thank you for the bisect and the per-run fault counts; that makes this much easier to pin down. #6678 is mine. I'm reading
rewatchSettledImportsagainst your table now, together with my open #8926, which also returns retired small imports to the write watch, so it needs the same check before it goes anywhere. I'll post a branch here for you to run on Grand Theft Auto V (PPSA04264), with what we'd like counted.tibzejoker commentedon Oct 21, 2026
Thanks @EagleBearerYS. I'll run your branch on GTA V as soon as it's up, with the same per-run fault counts and types. A first-parent bisect of the 85
MEM_MAPPEDfaults left withAPS5_NO_DEFERRED_SETTLES=1is running now. I'll post which merge it lands on here.AI-assisted: yes (Claude Code)
Dripl1x commentedon Oct 21, 2026
@tibzejoker, thanks. #7473 is mine, and for it I'd take option 2: stop arming resident runs.
armResidentmakes clean resident runs (MEM_PRIVATE, 256 KiB and up) read-only so that a collect can skipGetWriteWatch, and the first guest store into each run is then resumed on the guest thread. That is the #361 fault that #5596 removed. It happens at most once per run per mapping, which fits your 44 to 89 per run. Option 3 would not cover it, since these are private pages, not shared views.There is no runtime switch for it. I'll post a branch here that removes the arming, so resident memory is collected with
GetWriteWatchonly, as before #7473. Expected on your counter: 0MEM_PRIVATEfaults per run,MEM_MAPPEDunchanged. #7471 stays as it is.If your bisect of the remaining 85
MEM_MAPPEDfaults lands on #7473 or on my #8857, I'll take that too.AI-assisted: yes (Claude Code)
Dripl1x commentedon Oct 21, 2026
@tibzejoker, the branch:
Dripl1x/AnyPS5fix/no-resident-write-faults, one commitd6e270ea9fon main80565c5942.armResidentandresumeResidentWrites: resident runs are never write-protected and are collected withGetWriteWatchonly.HostImportAccessstays and is now a no-op on resident pages, soView::hostImportsis only counted, never read.CheckLargeResidentWritesStopFaultingis nowCheckLargeResidentWritesDoNotFault: a 2 MiB resident mapping staysPAGE_READWRITEafter collects, and 16 write/collect rounds over every page raise no access violation. With main'sWindowsMappings.hppit fails at the first protection check (GuestMemory.cpp:1773: after two clean collects the run is armed read-only).guest_memoryandguest_mmap_fixed_anonymous3/3;agc_driver_host_import_windows,agc_driver_host_import_at_map,agc_driver_host_import_teardown,agc_driver_host_import_failure_handlesandagc_host_import_retire3/3;agc_host_import_sparseskipped itself (exit 77);agc_resident_present,agc_host_import_unmap,agc_host_import_retirement,agc_host_import_second_mapping,agc_parked_import_unmap,agc_sparse_run_import,agc_sparse_run_import_copied,agc_driver_host_import_lifetimeandagc_driver_host_import_mutation_race1/1. No title was run on this branch. The change is Windows-only.What I'd expect on your counter: 0
MEM_PRIVATEresumed faults per run. The cleanest comparison is yourAPS5_NO_DEFERRED_SETTLES=1row: 133 (85MEM_MAPPED, 44MEM_PRIVATE) should become 85 and 0. With deferred settles on, the #6678 faults remain until @EagleBearerYS's branch.AI-assisted: yes (Claude Code)
EagleBearerYS commentedon Oct 21, 2026
@tibzejoker thank you again: the bisect and the per-type fault counts are what pinned this down.
What #6678 does, in the code. When a small import's deferred settle completes,
rewatchSettledImportsputs its range back in the write watch (GuestMemory::Rewatch). At that point Grand Theft Auto V (PPSA04264)'s buffers are still resident direct memory: private pages tracked withGetWriteWatch, which take no fault. That's why your MEM_PRIVATE check at rewatch time changed nothing. Later, a second mapping of the same direct memory turns the run into 16 KiB shared views (WindowsMappings::demote). From then on, every clearing collect over the range makes all its aliases read-only, so each guest store faults and is resumed, with #361's damage to the red zone. Before #6678, the import kept the range out of the write watch for good, so it was compared and never armed.My open #8926 (retired imports back in the write watch) calls the same
Rewatch, so it can do the same, and it would also bring such ranges back withAPS5_NO_DEFERRED_SETTLES=1. It stays on hold until it has this fix as well.The branch:
EagleBearerYS/AnyPS5@fix/agc-rewatched-imports-compared-when-shared(d98ee15, two commits onmain27bcd6a). It's your option 1, applied where the memory turns shared:Rewatchmarks the pages of each range it puts back in the watch.GetWriteWatch, as perf(agc): settle Windows imports under 1 MiB without waiting #6678 intended.windows_write_watchgains a case, and the newagc_rewatched_direct_sharedmaps direct memory, rewatches a block of it as a settle does, then maps the same memory a second time. The block must not be armed, a store into it must not fault, and the store must show through both mappings.How it fits with #7473: @Dripl1x's
fix/no-resident-write-faults(d6e270e) is the resident side, so resident runs are no longer armed. This branch only covers memory that turns shared after a rewatch, so the two don't overlap, and they merge cleanly. Your table suggests both are needed to reach zero.Could you run these four, with your fault-log patch, the same save and at least 60 s of Story?
main;main+ this branch;main+ this branch withAPS5_NO_DEFERRED_SETTLES=1;main+ this branch + Dripl1x's.Please post the fault counts by type (MEM_MAPPED, MEM_PRIVATE) and how many land in zlib, as in your table.
tibzejoker commentedon Oct 21, 2026
The bisect of the remaining
MEM_MAPPEDfaults lands on #8172 (@wachi35676, "keep resident DCC targets watched under aligned block imports", merge d9df34d).Same counter as above, with
APS5_NO_DEFERRED_SETTLES=1so that #6678 is out of the picture. Each row counts theMEM_MAPPEDresumed faults over 60 s of Story:MEM_MAPPEDresumed faultsgit revert -m 1 d9df34d08, clean)So #8172 accounts for these faults on its own; the single fault in the last row is the background level we have always seen. My reading of the change: an aligned block import now keeps the resident parts of its run in the write watch instead of unwatching the whole run. When the game then maps that memory at a second address, the run moves into the section and becomes shared views. Those views are tracked by write protection, so the guest's own stores fault (eboot
0x162b9e4,0x28f413a, outside zlib in this window, but the same #361 hazard). Before #8172 the whole run left the watch, so the views were never armed.#8172 fixes a real test failure (#8103,
agc_driver_msaa_dcc_clear_4), so reverting it is not the answer. One option is to keep watching only the parts that stay private (resident,MEM_WRITE_WATCH), and drop them from the watch at the moment they become shared views. I'll test whatever branch you prefer.@Dripl1x: your
fix/no-resident-write-faults(d6e270e) is reviewed here and queued. I'll run it as soon as this machine is free and post the counts.AI-assisted: yes (Claude Code). Measured on the machine in the first post.
tibzejoker commentedon Oct 21, 2026
@Dripl1x,
fix/no-resident-write-faults(d6e270e, on main 80565c5) on GTA V: 0MEM_PRIVATEresumed faults, as you expected. One run of 150 s of Story each, same counter and machine as above:MEM_MAPPEDMEM_PRIVATEAPS5_NO_DEFERRED_SETTLES=1Two follow-ups to my earlier posts:
MEM_MAPPEDfaults that I attributed to fix(agc): keep resident DCC targets watched under aligned block imports #8172 are gone on your branch with deferred settles off, even though 80565c5 contains fix(agc): keep resident DCC targets watched under aligned block imports #8172. On 28f1266, reverting fix(agc): keep resident DCC targets watched under aligned block imports #8172 did take the count from 134 to 1. So on newer trees, what fix(agc): keep resident DCC targets watched under aligned block imports #8172 exposed seems to go through the resident-run arming that your branch removes. @wachi35676, there may be nothing left to do on your side. I'll re-check on the next main.AI-assisted: yes (Claude Code). Measured on the machine in the first post.
EagleBearerYS commentedon Oct 21, 2026
@tibzejoker thanks, those two rows settle it. With Dripl1x's d6e270e, the MEM_PRIVATE faults and the ones you'd pinned on #8172 are gone, so what's left with deferred settles on is #6678's path. That's what #9159 (d98ee15) targets, so we've left the branch as it was.
Could you run one more, the same way as your second row (150 s of Story, deferred settles on, the default)?
main+ Dripl1x's d6e270e + fix(agc): keep a rewatched import's memory compared once it is shared #9159 at d98ee15.We expect the 394 MEM_MAPPED faults from settled imports in that row to go, and the zlib ones with them.
If you have time for a second run,
main+ #9159 alone (without Dripl1x's fix) would show the two fixes apart.fix(agc): keep an import's resident memory compared once it is shared
tibzejoker commentedon Oct 21, 2026
@EagleBearerYS, both runs, on
main663de1a, 150 s of Story with deferred settles on (the default). Same counter and machine as above:MEM_MAPPEDMEM_PRIVATEmain+ #9159 alone (98adb5f, d98ee15)main+ Dripl1x's d6e270e + #9159MEM_MAPPEDresumed fault. The 111 left are perf(libc): skip write-watch queries for proven clean resident ranges #7473's resident arming; 45 of them land at zlib's eboot0x28abdd4, and the run crashed.MEM_MAPPEDfaults come back. None of them is in zlib. Their sites are eboot0x28f413a(18),0x162b9e4(10), and two in a system DLL (+0x…426910,+0x…42214). Those are the same sites as the 85 I first saw with deferred settles off, which perf(libc): skip write-watch queries for proven clean resident ranges #7473's arming seemed to hide.comparedWhenShared, and their views get armed.MEM_PRIVATEfault in the combined row is at0x28abdd4. It is probably the race path inHandleWrite, where the page was already writable when the handler ran.One run per row. These counts are stable in kind from run to run, but not in exact number.
AI-assisted: yes (Claude Code). Measured on the machine in the first post.
EagleBearerYS commentedon Oct 21, 2026
@tibzejoker thank you, that pair is exactly what was missing. Your reading fits: the 42 MEM_MAPPED faults at
0x28f413aand0x162b9e4are on resident runs that an import kept in the write watch. That's the path #8172 changed so that resident DCC targets stay watched, which #8103 needs. Those runs are never rewatched, so #9159's first two commits don't mark them.The third commit covers them.
EagleBearerYS/AnyPS5@fix/agc-imports-compared-when-sharedis at 79cae21, one commit on top of #9159's d98ee15, and #9159 takes it next:ImportWatchedmarks the resident pages of each run it keeps in the watch.Could you run one more, the same way (150 s of Story, deferred settles on)?
main+ Dripl1x's d6e270e + 79cae21.We expect MEM_MAPPED at your background level. The single MEM_PRIVATE fault in your last run is on the resident side, so it's yours and Dripl1x's to judge.
fix(agc): keep an import's resident memory compared once it is shared
tibzejoker commentedon Oct 21, 2026
@EagleBearerYS, a run on
main663de1a + Dripl1x's d6e270e + your branch, 150 s of Story with deferred settles on. Your branch head is now 81aec58 (with ead292e under it, on top of d98ee15), not 79cae21, so I ran that head.MEM_MAPPEDMEM_PRIVATEmain+ d6e270e + #9159 (d98ee15)main+ d6e270e + your branch (81aec58)0x162b9e4is gone too.MEM_MAPPED:0x28f413a(71 faults): amov %rax,(%rsi,%rdi,8)inside a loop that keeps state inr12–r14, so it is not a red-zone leaf at that point.+0x…4269(34 faults): host code, which does not use a red zone.From my side, the crash is fixed with the two fixes together. Whether to widen the rule to direct memory that was never imported, so that the remaining sites stop faulting too, is your and the maintainers' call. That is the most general form of #361, and I'll test it if you go that way.
AI-assisted: yes (Claude Code). Measured on the machine in the first post.
EagleBearerYS commentedon Oct 21, 2026
@tibzejoker thank you for the run and the whole bisect: 0 MEM_PRIVATE, 0 faults in zlib, and no crash is the result we were after.
Grand Theft Auto V (PPSA04264)'s crash needs both fixes:
The 105 MEM_MAPPED that remain (
0x28f413a, and the system DLL's host code) we're leaving as they are for now. By your reading they're direct memory that was never imported and is mapped twice. That memory is armed, as it was before any of these changes. Per your notes, neither site reloads pointers from a red zone, so they don't carry #361's crash. Comparing all such memory instead of arming it would cost every title that maps direct memory twice: its collects would return nothing, so GPU caches over it would compare on every use, and address-based builds would copy it. We'd widen the rule only for a title that crashes at a site of that kind.@Dripl1x, are you opening a PR for d6e270e? Then it and #9159 can name each other as the two halves of #9136.
Dripl1x commentedon Oct 22, 2026
@EagleBearerYS yes, it's #9181 (the same commit d6e270e, unchanged). Its description names #9159 as the
MEM_MAPPEDhalf of #9136, so #9159 can point to #9181 instead of the branch.11 remaining items
EagleBearerYS commentedon Oct 23, 2026
@embersas thank you. A standalone reproduction with no AnyPS5 or title code, on different hardware, is useful independent evidence for the mechanism, and the owned controls through the production handler confirm it in our build too.
Testing the two PRs on your workstation would help a lot. In order of usefulness:
main:test/9159-9181-togetheron EagleBearerYS/AnyPS5 (https://github.com/EagleBearerYS/AnyPS5/tree/test/9159-9181-together, d7cec80 =mainef0d8ae + fix(agc): keep a rewatched import's memory compared once it is shared #9159's 1ad5f29 + fix(libc): stop write-protecting resident runs on Windows #9181's d6e270e, no conflicts). Please run the cases you listed: the resident no-fault case, rewatched or imported pages that become shared, remapping, and import-created aliases, with the unmarked shared-view control kept.For each case, the most useful result is whether the store still faults, and whether the bytes below RSP changed. Keeping the remaining shared-view faults apart, as you suggest, is right: #9159 leaves some views armed on purpose. Those remaining faults are what the opt-in red-zone framing in #5640 (reopened as a draft) is for.
We'd welcome the standalone reproducer too. #5640's execution test simulates the clobber by filling
[rsp-0x80, rsp)before each instruction. A real resumed fault on Windows would test the framed leaves against the frame Windows actually writes. If you can share it (a gist or an attachment here) under a license compatible with the project's GPL-2.0, and we adapt part of it into a test, we'd credit you as co-author on that commit.embersas commentedon Oct 23, 2026
@EagleBearerYS thanks. I ran both PRs on the same workstation (Ryzen 9 3900X, Windows 11 26300.9711, XState 0x7, WinLibs GCC 15.2.0 r7, Release). These were CPU tests only. No game was launched for any of this.
Trees, all with the PR heads unchanged (d6e270e, 1ad5f29):
main0a0ed72 alone, with fix(libc): stop write-protecting resident runs on Windows #9181, with fix(agc): keep a rewatched import's memory compared once it is shared #9159, and with bothtest/9159-9181-togetherat d7cec80, as givenBoth PRs applied to 0a0ed72 with no conflicts. The write-tracking implementation and test source files checked in the provenance record are byte-identical between my combined tree and your branch; the branches use different main revisions.
Focused tests, 3 runs per tree:
guest_memory,guest_mmap_fixed_anonymous,windows_write_watch,agc_write_tracking,agc_write_tracking_metadata_commit,agc_resident_direct_remap,agc_rewatched_direct_shared,agc_import_alias_shared,agc_host_import_retire. All pass where registered.agc_host_import_sparseskips (Linux only).For your per-case question I used a small probe. It is linked against each tree's own libc and agc
GuestMemory.cpp, and sets up memory the way your tests do. Then it makes one 32-byte store with 16 known qwords in[rsp-0x80, rsp), at rsp mod 64 = 24, and lets the productionGuestArenaHandleWriteresume any fault. Each cell is "store faulted / qwords changed below rsp". There were 90 independent probe executions: 6 case modes x 3 repetitions x 5 trees. Some modes also perform the embedded shared-view controls shown below; those rows are not additional executions. Outcomes were consistent across repetitions. The resident row describes the first store after clean collects; on baseline and #9159 alone, later stores in that same mapping do not fault after its first resume:The probe verified the stored bytes in every case and visibility through the second mapping in the shared cases. For resident memory the next collect still detected the written page. In the four covered #9159 shared cases, collection refused as intended and the mappings stayed writable. I did not exercise the caller's subsequent comparison, so end-to-end dirty-data detection through that fallback remains unverified.
Failing controls, one at a time in a disposable tree with your tests kept:
WindowsMappings.hppreverted tomain:guest_memoryfails at the PAGE_READWRITE check (line 1790 on thismain), and the resident store faults again.windows_write_watch,agc_rewatched_direct_sharedandagc_import_alias_sharedfailRewatchmark: onlyagc_rewatched_direct_sharedfailsImportWatchedmark: the imported case andagc_import_alias_sharedfailwindows_write_watchfails, at the remap casedemote(..., true): onlyagc_import_alias_sharedfailsI found no correctness defect within this tested CPU scope, and the observed fault/red-zone outcomes match the stated scope of both PRs, alone and together. The unmarked shared views still fault and still lose the red zone on every tree, which I read as the class you left for #5640. The standalone reproducer still shows the overwrite (12/12 fenced runs), so nothing here changes what Windows does. It only shows which stores no longer fault.
What I did not test:
breakRunand the whole-run demotion paths you mentionedGetWriteWatchskipOne small thing for #9181, if useful:
docs/dev/CONVENTIONS.mdline 23 still says stable resident runs of at least 256 KiB can be read-only on Windows. You know this code far better than I do, so I may be misreading it.If a different case or ordering would be more useful, I can run it.
AI-assisted: yes.
EagleBearerYS commentedon Oct 23, 2026
@embersas thank you, this is exactly the separation we hoped for: each PR alone, both together, and the failing controls piece by piece. I've added your table to #9159's Tested section as independent CPU-only validation, with the limits you listed.
If you have time for more, two things would help most, in this order:
test/9159-9181-together, and onmainfor comparison:agc_driver_host_import_lifetime,agc_driver_host_import_lifetime_other_family,agc_driver_host_import_mutation_race,agc_driver_host_import_failure_handlesandagc_driver_guest_memory_lifetime_converted. They go through the import paths both PRs change, with a real device.breakRunand the whole-run demotion matter less to us for now.tibzejoker commentedon Oct 23, 2026
@EagleBearerYS here are the GTA V runs on
test/9136-diag-on-9181.Build: 2b875be + ed8a900 (#9730's voice fix; without it the branch stops at
sceVoiceSetThreadsParams) + a local per-rip resumed-fault log. Relinked without--frame-red-zone, withAPS5_MIMALLOC=1. Three open-world runs of up to 420 s on an RTX 4090 with Windows 11.[shared-arming]linesinflate_fast0x28aa5e0inflate_fast0x28aa898alias)So each crash again follows a single resumed write on that thread.
Run 2: the alias case, unmarked.
comparedWhenShared 0.ImportWatchedmarks only the spans thatwalkWritesaccepts, so this one may have fallen outside them.armed by none: noCollectre-arm loop armed it, yet the subpage was read-only when the write came. The protection may come from the demotion itself, or from aprotectSubpagescall outside that loop.Run 1: a resumed write that never reaches
[shared-arming].HandleWrite. My fault log runs after the resume, soprotis the protection after the handler.HandleWrite(it returns true when the page is already writable, with no print), or a handler outsideWindowsMappings. Printing in the resident branch too would tell which one it is.Since the fault in run 1 does not go through shared-view arming at all, framing (#5640) looks like the only fix here that covers every source. The full logs are kept here if you want other lines.
EagleBearerYS commentedon Oct 23, 2026
@tibzejoker thanks, these narrow it down.
demote()makes the page read-only for the switch, and a guest store that lands in that window faults beforeImportWatchedmarks the page. That would also explaincomparedWhenShared 0witharmed by none. A mark can't prevent that window; only framing makes the resumed write harmless.WindowsMappings: the page is private, and the shared-view branch ofHandleWritenever sees it.test/9136-diag-on-9181now has two more lines, at 594b624, that would settle both: a[shared-arming] resident write …line for writes the resident branch resumes, and, for a resumed shared-view write, whether its subpage was armed and how many milliseconds after its demotion the write came. If you have time, one more run of that branch would tell.Framing covers both sources, and your backward extension is going into #5640 as a commit that credits you.
tibzejoker commentedon Oct 23, 2026
@EagleBearerYS three more open-world runs on 594b624 (same build otherwise: + ed8a900 + the per-rip fault log, no
--frame-red-zone,APS5_MIMALLOC=1).inflate_fast0x28aa8abRun 3 fits the race you described:
demote(), not arming.comparedWhenSharedis already 1 when the line prints, so the mark was in place by then. It still could not stop the fault.[shared-arming] resident writeline appeared in any of the three runs. The one MEM_PRIVATE fault of the earlier batch did not recur, so its handler is still unknown.Over the six diagnostic runs, 3 crashed, each right after a single resumed write in
inflate_faston the crashing thread; the 3 runs without a resumed write did not crash. With the framed exe (#5640 + the backward extension) it was 0 crashes in 4 runs, with resumed writes in two of them.EagleBearerYS commentedon Oct 23, 2026
@tibzejoker thanks, that settles run 2's case: the write came 0 ms after the alias demotion, on a subpage nobody armed, with the mark already in place, so it's
demote()'s own read-only window, and no mark can close it. Across your six runs every crash followed a single resumed write ininflate_faston the crashing thread, and with the framed executable it was 0 crashes in 4 runs, two of them with resumed writes. That points to framing as the fix for this crash. Your backward extension is being built into #5640 now, with its test changes, and I'll ask you for a run when it's up.embersas commentedon Oct 23, 2026
@EagleBearerYS closing the loop on your GPU and comparison-fallback requests above: the original results are on #9159, and the retest of your corrected head
cb9de8049cis here.The host-import GPU tests passed on the original baseline and combined branch. The fallback check found a stale first texture lookup; the corrected head now passes all four fixture orderings, three times each, and the new upstream regression test passes 3/3. The old combined branch still reproduces the stale read in both affected orderings, 3/3 each. No Vulkan validation errors or warnings on the RTX 3080 Ti.
This confirms the sampled-texture fallback fix within our synthetic test scope, not a fix for GTA V's crashes or every remaining red-zone fault. No game was tested.
AI-assisted: yes.
EagleBearerYS commentedon Oct 24, 2026
@tibzejoker #5640, the relinker's red-zone framing for leaves without a frame pointer, is merged in
mainas of d01070d, with the same framer your four framed runs used, so it may address this crash. It only runs when Grand Theft Auto V (PPSA04264) is relinked for Windows with--frame-red-zone, which is off by default, so could you retest on currentmainwith that flag and the per-ripfault log on, and say which base you used and whether the HDD Streamer crash after a resumed write ininflate_fastcomes back? On plainmain, without #9181, #9159 and #9445, a run may stop somewhere else first, such as theState.cppalpha-to-coverage throw your run 2 hit.fix(agc): keep a rewatched import's memory compared once it is shared
fix(libc): compare the resident runs an import's alias demotes once t…
tibzejoker commentedon Oct 24, 2026
@EagleBearerYS here is the retest on current
mainwith--frame-red-zone.Base.
mainc8d34aa (which includes #5640's d01070d and #9159), nothing else merged in. The only local change is the per-ripresumed-fault log in libc'sCrashReport.cpp(diagnostic, never for upstream). Relinked with--windows --to-intel --frame-red-zone,APS5_MIMALLOC=1, RTX 4090 (616.92), i9-13900K, Windows 11. Three open-world runs of 420 s each from the same save.Relinker:
243 framedof 991 leaves that keep values below RSP, with the same skip counts as on #5640's head (642 "exit shorter than a jump", 58 "other use of rsp", 17 "branch leaves the function", 16 "prologue shorter than a jump", 7 "indirect jump", 7 "overlaps another stub", 1 with no room for a stub).inflate_fastis among the framed leaves (same counts as on cec1f82, where I checked it in the output).inflate_fast(lower bound)The log prints the first three hits of each
rip, then the 10th, 100th and so on, so the per-ripcounts are lower bounds.inflate_fast. Without framing, a single one was enough to crash that thread.mainresumes far more faults than the stack I used before. There are about 1,000-1,200 per run here, against a few with fix(libc): stop write-protecting resident runs on Windows #9181 and fix(agc): keep a rewatched import's memory compared once it is shared #9159. Almost all areMEM_PRIVATE(type 0x20000): the top site is eboot 0x28abdd4 (≥ 100 hits, 256 KiB regions), the resident-run arming of perf(libc): skip write-watch queries for proven clean resident ranges #7473 that fix(libc): stop write-protecting resident runs on Windows #9181 removes. Only oneMEM_MAPPEDwrite showed up, in run 1.main, framing is what keeps GTA V alive, and fix(libc): stop write-protecting resident runs on Windows #9181 is still worth having for the fault count itself.State.cppalpha-to-coverage throw in these three runs.AI-assisted: yes.
EagleBearerYS commentedon Oct 24, 2026
@tibzejoker thank you for the retest. On
mainc8d34aa with--frame-red-zone, Grand Theft Auto V (PPSA04264) ran three 420 s open-world runs with no HDD Streamer crash, each with at least 15 resumed writes insideinflate_fast, which #5640 frames. Framing is opt-in: a build relinked without--frame-red-zoneleaves those leaf functions unframed. Whether this issue can close is your call. Your fault counts on plainmain, about 1,000 to 1,200 per run and almost all from the resident-run arming, are a data point for #9181.tibzejoker commentedon Oct 24, 2026
Closing: on current
main, a GTA V (PPSA04264) build relinked with--frame-red-zoneno longer hits the HDD Streamer crash (3 × 420 s open-world runs, each with at least 15 resumed writes insideinflate_fast, details above). Thanks @EagleBearerYS and @Dripl1x.What remains, for anyone landing here:
--frame-red-zoneleaves those leaves unframed, and a single resumed write ininflate_fastcan still crash the streamer.mainresumes about 1,000-1,200 write faults per run, almost all from the resident-run arming (perf(libc): skip write-watch queries for proven clean resident ranges #7473) that fix(libc): stop write-protecting resident runs on Windows #9181 removes. Fewer resumed faults also means fewer chances for any other code that keeps data belowrsp.