mirror of
https://github.com/Comfy-Org/comfy-aimdo.git
synced 2026-11-04 23:37:21 +00:00
[GH-ISSUE #75] Fix CUDA 13.0 crash: replace Detours with manual hooks + fix RIP-relative overflow via near allocation #24
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/comfy-aimdo#24
Loading…
Add table
Add a link
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?
Originally created by @hahajaja on GitHub (Jul 15, 2026).
Original GitHub issue: https://github.com/Comfy-Org/comfy-aimdo/issues/75
comfy-aimdo CUDA 13.0 Hook Fix — Full Report
Author: Community contributor (openwork)
Date: 2026-07-23
Test duration: 7+ days of daily use, no crashes or CUDA errors
Since I'm not a professional programmer and the code has been modified by AI, the fixes may only apply to my own case. This post is intended solely for sharing ideas.
Platform
gace72abef)Table of Contents
1. Detours Incompatibility with CUDA 13.0
Symptom
ComfyUI crashes at startup with an access violation:
Root Cause
DetourAttachreturns error code 8 (ERROR_NOT_ENOUGH_MEMORY) for all 6 hooks. The investigation tree was:cuMemAlloc/cuMemFreeto force IAT resolution did not help — all IAT entries were already resolved.cuGetProcAddressreturns real function body addresses insidenvcuda64.dlldirectly, not JMP stubs innvcuda.dll. The function prologue is48 83 ec 28 81 3d <disp32> <imm32>(14 bytes) for all 6 targets..detourc/.detourdsection mechanism and helper-DLL trampoline allocation cannot handle the CUDA 13.0 driver's function layout.DetourAllocateRegionWithinJumpBoundsreturns NULLDetourCopyInstructionfails on the first instructionDetourSetSystemRegionLowerBound/UpperBoundare not exported fromdetours.libResolution
Abandon Detours entirely. Implement a manual x64 code hook.
Hooking flow:
saved[]buffersub rsp, 0x28(4 bytes) verbatim to trampolinecmp [rip+disp32], imm32with absolute addressing (see Fix 2)target+14at the end of the trampolinesaved[]and free trampolinesFiles changed:
src-win/cuda-detour.c— complete rewrite from Detours-based to manual hookssrc-cuda/dispatch.c— reverted the ineffective IAT pre-resolution patchdetours.lib2. alloc_near Failure & RIP-Relative Overflow
Symptom
After Fix 1 was deployed, the hooks would install successfully on some boots but fail on others with ASLR:
Root Cause (4 Layers)
Layer 1 — 132 GB distance overflow
VirtualAlloc(NULL, 64, ...)returns addresses in the low range (0x000001...), whilenvcuda64.dllloads at high addresses (0x7FFB...). The difference is ~132 GB, far exceedingint32_t(±2 GB). Thecopy_rip_rel_instfixup (src - dst) truncates, producing a wrong displacement for thecmp [rip+disp]instruction — pointing to an unrelated global variable.The program booted without crashing (the
cmplikely checks a lazy-init flag that was already set), but during inference the comparison read garbage memory, potentially causing undefined behavior.Layer 2 — VirtualQuery 64 KB alignment bug
The forward scan used 64 KB align-down:
After the first hook allocated at
0x...730000, the next MEM_FREE region started at0x...731000. Aligning0x...731000down produces0x...730000(already allocated), soVirtualAllocfails.Fix: forward scan must align up; backward scan must align down.
Layer 3 — 256-iteration cap
The scan loop had an artificial 256-iteration limit. On some ASLR layouts, CUDA + PyTorch load enough DLLs and memory-mapped files to fill the 2 GB neighbourhood with committed pages, requiring more than 256
VirtualQueryiterations to exhaust the address range.Fix: replace the iteration cap with address-range bounds (
target ± INT32_MAX).Layer 4 — No MEM_FREE found at all
After fixing Layers 1-3, the scan log showed:
The only MEM_FREE region after
nvcuda64.dllwas 4 KB — insufficient for a 64 KB-alignedMEM_COMMIT | MEM_RESERVEallocation. The subsequent 2 GB range was entirelyMEM_COMMITpages (a dense layout caused by CUDA 13.0 + PyTorch 2.10 + Windows 11 process initialization).No amount of VirtualQuery tuning can find a suitable free slot because one does not exist within the ±2 GB window.
Resolution
Abandon RIP-relative fixup entirely. Use absolute addressing.
Since the trampoline no longer needs to be near the target,
alloc_nearbecomes trivial:Trampoline layout (36 of 64 bytes used):
Files changed:
src-win/cuda-detour.c—alloc_nearsimplified from 147 lines of VirtualQuery scanning to 1 line;copy_rip_rel_instreplaced byemit_abs_cmp3. Robustness Fixes
Three additional issues were found and fixed during code review.
3a — RAX Clobber
Risk: The original instruction
cmp [rip+disp32], imm32(81 3D ...) does not modify any general-purpose register. Our replacementmov rax, <global_addr>overwrites RAX, potentially breaking code that uses the caller's RAX value after the prologue.Fix: Wrap the absolute-addressing sequence with
push rax/pop rax(2 extra bytes, 36 B total trampoline size).3b — Opcode Check Too Permissive
Risk:
0xC7 = 11000111only preserves Mod (bits 7:6) and R/M (bits 2:0), masking out Reg (bits 5:3). The81opcode's/digitselects the operation:/7= cmp (our target)/0= add,/1= or,/2= adc, etc.This would match
add [rip+...], imm32just as easily ascmp. While all 6 current targets are indeed81 3D(cmp), this is an implicit assumption.Fix:
81 3D= ModRM00 111 101= Mod=00, Reg=111(cmp), R/M=101(RIP-relative).3c — VirtualProtect Return Value Not Checked
Risk:
If
VirtualProtectfails (security policy, debugger protection, read-only pages), the subsequent write causes an access violation in an already-partially-patched state.Fix:
The
fail:unwind path restores all earlier hooks fromsaved[]and frees their trampolines:Final Code State (
src-win/cuda-detour.c)The complete file is 153 lines. Key components:
alloc_nearVirtualAlloc— no more scanningwrite_abs_jmpFF 25 00 00 00 00 <8-byte>absolute indirect JMPemit_abs_cmpcmpwith RAX preservationinstall_hook_entriesaimdo_setup_hooksinit()Hooks are no longer linked against Detours (
detours.libremoved from linker input). All hooking is done viaVirtualAlloc+VirtualProtect+ raw byte manipulation — zero external dependencies.Test Summary
6 hooks installedon every boot, no crashes, no CUDA errors@coderabbitai[bot] commented on GitHub (Jul 15, 2026):
⚠️ Possible Duplicate Issue(s)
🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
@localbobthebuilder commented on GitHub (Oct 3, 2026):
We hit the same failure independently on a different setup and can confirm the root cause with a deterministic reproduction. Sharing the evidence in case it helps decide the fix direction.
Our setup: Windows 11, RTX 5090, torch 2.14.0+cu130, comfy-aimdo 0.5.5, ComfyUI v0.37.4. It reproduced with the stock wheel and with local test overlays, so it is not specific to any patch.
Symptom: three crashes (
0xC0000005) in thecuMemAllocAsyncpath during model loading; minidumps retained.Decoded fault:
nvcuda64.dll!cuMemAllocAsync+0xE.48 83 EC 28 81 3D EA79B201 00BA1C32(sub rsp,28h; cmp dword ptr [rip+0x1B279EA], 0x321CBA00), with the guard atnvcuda64+0x1DC0678.0x7FA3FE1Cbelow the module (just inside the +/-2 GB window), while the guard is a further0x1DC0678into the image in the opposite direction.0x8180048A(2,172,650,634) - larger thanINT32_MAX. Detours stores(LONG)nNewOffsetinCDetourDis::AdjustTarget(case 4) and setsm_plExtra = 0; theASSERTbelow validates the 64-bit target, so the truncation is silent. The trampoline then readscorrect_guard - 2^32, which is unmapped, causing the access violation. This is exactly your "Layer 1" overflow, confirmed at the library level. Detoursmainstill contains this code.Deterministic reproduction (no GPU needed), pinned vs fixed:
Prototype Detours fix we validated locally: scan the prologue for addresses referenced by RIP-relative operands, tighten trampoline placement by those references, and make
DetourCopyInstructionfail instead of truncating when a relocation cannot be encoded (the attach fails cleanly rather than installing corrupted code). With it, both deterministic tests pass, Detours' own unit suite passes (132 assertions), and a local aimdo build ran 10/10 LTX 2.5 generations (queued, warm, changed prompt, 15 s) with no crashes.Limitation, for completeness: if a dense layout genuinely has no free region that reaches both the entry and the referenced data within +/-2 GB (your "Layer 4"), a placement fix cannot help; the absolute-addressing rewrite you describe removes that constraint entirely. The two approaches fix different levels and are complementary.
For the maintainers: which direction would you prefer - (a) an aimdo-side fix along the lines of the absolute-addressing proposal here, (b) an aimdo change that carries/pins a Detours placement patch, or (c) a Microsoft Detours fix upstream with aimdo bumping the pin once merged?
We can share the deterministic C test and the Detours patch (small and self-contained).