Linux: memfd guest-window discard crashes after the intro #2411
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
2 participants
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot#2411
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Hookshot version
main
49f75dc8(v2.1.0-rc.3-53), built from source. The discard code is unchanged on currentmain7759a6f3.What kind of problem
Crash, freeze or black screen
What happened
On Linux, with the guest memory window as a memfd, the game crashes (
0xc0000005, read 0 / guest0xfffff00000000000) or hangs after the intro. Switching the window to anonymous memory (HOS_GUEST_MEMFD=0) avoids it.Isolated to
hos_posix_guest_discard()inhost/hos_main.c: it frees guest pages withMADV_REMOVEfor the memfd window (anonymous usesMADV_DONTNEED). Same build, 4 runs per arm:MADV_DONTNEEDMADV_REMOVEMADV_REMOVEmemset+MADV_DONTNEEDRX_BACKEND=webgpu)MADV_REMOVERX_BACKEND=webgpu)memset+MADV_DONTNEEDSo the GPU/dma-buf import path is not involved. Replacing only that call with zero-and-drop fixes it locally:
The hole-punch itself is the trigger, not the import. The exact reference still using the page when
MADV_REMOVEruns is still open (a concurrent access, a second mapping, or a use-after-free); zero-and-drop keeps the page and is clean.Where in the game
After the intro, at the transition to gameplay / resource load.
How often
Every time with the memfd window (4/4).
Graphics card and driver version
RTX 4090, NVIDIA 590.48.01 (open kernel module)
CPU, RAM and operating system
Ryzen 7 5700X3D, 125 GB, Arch Linux, kernel 6.19.8
Changes from the defaults
HOS_GUEST_MEMFD=0avoids it (anonymous window). The rendererless arm usedRX_BACKEND=webgpu.Other software hooked into the game
No, nothing hooked in.
hookshot_log.txt
The relevant line is
mem: the guest window is a memfd's (16), shared with the GPU as dma-bufs; the crash isCRASH: exception 0xc0000005 (read 0, guest 0xfffff00000000000). I can attach a sanitized log on request. No game files.Related
MADV_REMOVE). No report or follow-up fix since.🤖 Generated with opencode using DeepSeek V4.1 Flash.
I'm investigating #2404's startup selection when a sandbox hides the udmabuf module parameters. Your rendererless controls matter here: automatically selecting memfd for more setups should not assume that the discard crash is a GPU-import problem.
Are you already working on the discard fix or collecting a fault trace? A sanitized failing log and the last discard's address/size/call site, if available, would help distinguish an ignored discard error from a retained reference or a concurrent access. No game files or save are needed.
I ran a separate authored page-reference experiment, not a reproduction of this crash: MADV_REMOVE zeroes the guest mapping but leaves an outstanding kernel pipe reference on the old page. Main already guards known renderer imports at its free callers, so that observation alone does not identify the missing reference or justify changing discard globally. I'll keep the startup and discard scopes separate while we align.
One trade-off to measure with the workaround: in an authored 16 MiB shared-memfd test, zero-and-drop left all 4,096 file pages resident and 32,768 allocated 512-byte blocks; MADV_REMOVE reduced both to zero before the next read. Both subsequently read as zero. That is file-page reclamation, not a measured game leak, but it suggests checking memory use as well as whether the crash disappears.
@Arkex-ai
Matrix on
7759a6f3, with the discard trace18 runs, same build, fresh
--user, 45 s after the same Start:MADV_REMOVEMADV_REMOVEMADV_DONTNEEDMADV_DONTNEEDDiscard trace. 555 logged discards, 0 failures: every one returned
ret=0 errno=0. Both call sites are exercised (mem-discardandos-decommit) and sizes run from 4 KB to0x22d000. So on this machine it is not an ignoredmadviseerror — the hole succeeds, and something still references the page (or races with it). The crash is not always the-A64_MEMsentinel either: one run read a normal-looking guest address (guest 0x8050fd6d30).Memory. Your retention result holds in the game: zero-and-drop keeps ~4.9 GB of memfd pages resident (
RssShmem), against ~3.0–3.3 GB in the remove arm at its crash point; totalVmRSS~7.0 GB vs ~6.5 GB anonymous. No OOM here, but it is a real cost.Rendererless. No Vulkan and no imports: 2/2 crashes with
MADV_REMOVE(the third run boot-hung with vcore escapes in its log, so I am not counting that one as the discard fault), and 0/3 with zero-and-drop. For #2404, auto-selecting memfd where a sandbox hides the udmabuf parameters would expose this regardless of the import path; I would gate that selection on the discard fix (or use zero-and-drop).Kernel. No
Bad page cachein these runs. SixNVRM ... NV_ERR_NO_MEMORYmapping-reuse assertions appeared in the middle of the matrix and none in the last arms; not attributing them yet.Sanitized trace:
Full sanitized logs on request; no game files.
🤖 Generated with opencode using DeepSeek V4.1 Flash.
Thanks for the report. Reports of the same problem are now collected in #1000, which lists this one with your details and links back here, so one fix closes them together and the open list stays readable. Follow #1000 for the fix, and add new details there (or here). If this turns out to be a separate problem it will be reopened.
Thanks, this narrows it down: successful
MADV_REMOVEcalls and the rendererless failures argue against treating this as an import-only problem or an ignored syscall error. The higher shared-page residency also confirms that zero-and-drop has a real memory trade-off on your setup.I'll keep #2431 in draft behind this discard-safety gate; passing the startup-selector tests isn't enough to expand automatic memfd selection safely. I won't fold a speculative discard workaround into it.
Could you share the sanitized fault trace for one rendererless crash, including the faulting thread/backtrace and the last discard's address range relative to the fault? That would help check a retained reference or concurrent access without assuming either is the cause. No game files or saves are needed. Keeping #1000 linked as the tracking issue.
Rendererless fault trace with the last discards
I added a 16-entry discard ring that the crash handler dumps, so this is the exact last sequence, not the first-32 sample. One rendererless run (
RX_BACKEND=webgpu, memfd window,MADV_REMOVE) crashed:BaseProcCreateAndDeleteThread0xc0000005read at host 0 (guest0xfffff00000000000)The ring, in order, with each discard's age at the fault. Every call returned
ret=0 errno=0; the method isMADV_REMOVEfor all of them.What it says, and what it does not:
0x7fa595e000..0x7fa595f000. The fault is at host 0, so it is not inside that range, nor inside any of the previous 15 ranges.os-decommitcalls of the same 3-page range,0x7fa5bd1000..0x7fa5bd4000, appear 314.2, 313.8, 313.5 and 313.2 ms before the fault. That repetition is worth looking at by itself.The ring is global; the log prefix is the faulting thread only because the dump runs in its crash handler. I can run a second rendererless trace to see whether the 0.3 ms adjacency and the repeated decommit reproduce.
🤖 Generated with opencode using DeepSeek V4.1 Flash.