mirror of
https://github.com/Comfy-Org/comfy-aimdo.git
synced 2026-11-04 23:37:21 +00:00
[GH-ISSUE #97] Segfault in hostbuf_free() during RAM-pressure cache eviction (HostBuffer.__del__) #34
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#34
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 @Romic77 on GitHub (Aug 25, 2026).
Original GitHub issue: https://github.com/Comfy-Org/comfy-aimdo/issues/97
Summary
A ComfyUI instance running with DynamicVRAM enabled dies with
SIGSEGVinsideaimdo.so'shostbuf_free(). The crash is reached fromHostBuffer.__del__,which is invoked when ComfyUI's RAM-pressure cache evicts a stale cache entry
holding the last reference to a
ModelPatcherDynamic(and therefore to itsdynamic_pinsHostBufferobjects).The process is killed outright —
faulthandlerprints the Python-level stack,then the interpreter is gone. It has happened twice on the same host, both times
about 2 seconds after a new prompt was submitted, i.e. at the first
post-node-execution eviction of the previous prompt's cached models.
comfy-aimdo0.4.14 does not fix it: itsaimdo.sois byte-identical to0.4.13 (see Ruled out below).
Environment
aimdo.somd58a58ab558fac0b291cd4dbe1730947b7, Build ID1771a215de85c1a92a27615f7336a42cf7932e6f)CUDA_VISIBLE_DEVICESLaunch arguments of the crashing instance (GPU 0):
Note there is no
--cache-*flag, so the default RAM-pressure cache(
CacheType.RAM_PRESSURE) is in effect.Crash output
Identical stack on both occurrences (2026-08-24 15:03:27 CST, PID 2115801;
2026-08-25 15:17:01 CST, PID 2415539):
host_buffer.py:129is thelib.hostbuf_free(ptr)call inHostBuffer.__del__:How the crash path is reached
Reading the ComfyUI 0.33.0 source along the stack:
execution.py:799-811— after every node execution, whencache_type == CacheType.RAM_PRESSURE, the executor callsram_release_callback(ram_inactive_headroom).comfy_execution/caching.py:550RAMPressureCache.ram_release()builds acandidate list of cache entries whose
used_generation != generation(i.e. not touched by the current prompt) and assigns each an OOM score.
Entries holding a
ModelPatcherfrom an older generation are givenoom_ram_usage = 1e30with the comment "old ModelPatchers are the first togo", so they sort to the top of the eviction list.
caching.py:591-597— the eviction loopdel self.cache[key]drops the lastreference to that
ModelPatcherDynamic. Itsmodel.dynamic_pins[device]pin-state tuples (created atcomfy/model_patcher.py:1877-1880andcomfy/model_management.py:1453) eachhold a
comfy_aimdo.host_buffer.HostBuffer, so Python refcounting runsHostBuffer.__del__→lib.hostbuf_free()→ SIGSEGV.Why this path runs constantly on this host
main.py:412-417computes the two RAM-pressure thresholds from total RAM:ram_release()returns early only whenpsutil.virtual_memory().available >= target.With
ram_inactive_headroom ≈ 62 GiBon a 61 GiB machine, that condition cannever be satisfied, so the eviction loop executes after every single node and
frees every inactive entry it can find. On this host
availablesits around32 GiB, so the loop always runs to completion.
That matches the observed timing exactly: both crashes landed ~2 s after
got prompt, before any model-loading log line — i.e. at the first evictionfollowing the first node of a new prompt, when the previous prompt's
ModelPatcherDynamicobjects became stale.Ruled out
Not an OOM kill. No
oom-kill/Killed processentries in the kernel logor journal; at crash time the host had ~32 GiB RAM available and 103 GiB of
109 GiB swap free. The process died from
SIGSEGV, andfaulthandlerprinted aPython stack, which an OOM kill would not produce.
Not fixed in 0.4.14. The only release newer than 0.4.13 ships the same
native library:
host_buffer.pyis identical between the two versions.Not simply "freeing a degenerate buffer". Because the instance runs with
--disable-pinned-memory,MAX_PINNED_MEMORYstays at its initial-1(
comfy/model_management.py:1562), soreturns
0, and the pin-state buffers are constructed asHostBuffer(0, 64 MiB, 0)/HostBuffer(0, 8 MiB, 0)— a non-zeroprewarmwith
max_mmap_size == 0. We tested that combination in isolation:All of
(0,0,0),(0,8 MiB,0),(0,64 MiB,0),(0,8 MiB,2 GiB)and(0,64 MiB,2 GiB)allocate and free cleanly in a fresh process. So the crashneeds additional state — a buffer that has actually been
extend()ed andregister()ed, with live CUDA host registrations or in-flight transfers at themoment of free. That state is not reachable from a standalone script, which is
why we cannot offer a self-contained reproducer.
Frequency and impact
Two crashes in two days on the GPU 0 instance. A second instance on the same
host with an identical command line (only port, GPU, DB path, temp dir and
--disable-metadatadiffer) has run since 2026-08-19 without crashing, so thetrigger appears workload-dependent rather than configuration-dependent — both
instances execute the same
ram_releasepath on every node.The workload preceding the 2026-08-25 crash was a Flux2 graph using dynamic VRAM
staging, e.g.:
Impact is larger than a single failed prompt: the service is under
Restart=always, and reloading ~4260 custom nodes takes ~6 min 48 s before theHTTP port is bound again, so each crash costs about 7 minutes of downtime.
Suspected cause
We cannot see inside
aimdo.so, but the evidence points athostbuf_free()releasing host memory that is still referenced by the CUDAdriver — either a still-registered range (no
cudaHostUnregisterbefore themunmap/free) or a buffer whose decommit is racing an async copy. ComfyUI's own
comment at
execution.py:807acknowledges asynchronous decommit behaviour:A defensive
hostbuf_free()that unregisters and synchronises before releasing —or that tolerates being called on a buffer whose registrations were already torn
down — would likely stop the process from dying.
What we can provide
The host is still running and the fault is reproducible over a timescale of
days. On request we can supply:
comfy_aimdo.control.set_log_debug()output leading up to a crash(ComfyUI exposes this via
--verbose DEBUG).aimdo.sosymbols, if a debug build or symbol file ispublished — the shipped
.sois stripped apart from its Build ID.Secondary observation (ComfyUI side, not aimdo)
pinned_hostbuf_size()silently collapses to0when--disable-pinned-memoryis set, because
MAX_PINNED_MEMORYremains-1andmin(size, -1) * 2isnegative before the
max(0, ...)clamp. The pin-state buffers are then createdwith a non-zero
prewarmbut a zeromax_mmap_size. That combination does notcrash on its own (tested above), but it is almost certainly not the intended
configuration and may be worth guarding on the ComfyUI side.