Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

EXC_BAD_ACCESS in MPSGraph encodeToCommandBuffer during img2img (graph cache use-after-free)

Aperta
#50 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
45/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Tranquilla
Stack tecnologico
c

Direzione di ricerca

Inizia in iris_metal.m intorno a get_linear_graph_cache_bf16 alla riga 4692 e nei percorsi di encode alle righe 5691 e 5799; traccia come iris_gpu_batch_begin/end gestisce il command buffer condiviso. Riproduci img2img con immagini di riferimento a risoluzioni variabili, quindi verifica che l’espulsione dalla cache non causi più EXC_BAD_ACCESS mentre il lavoro GPU in sospeso fa ancora riferimento ai grafi precedenti.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Using commit https://github.com/antirez/iris.c/commit/9873887d4aa0646c650adc5b86b986d2f653b7e0

Crash

EXC_BAD_ACCESS (KERN_INVALID_ADDRESS at 0x8) during img2img generation with reference images:

MPSGraphOSLog
-[MPSGraphExecutable runInternalWithDevice:commandBuffer:feeds:results:executionDescriptor:mpsGraphOwnedCommandBuffer:]
-[MPSGraph encodeToCommandBuffer:feeds:targetOperations:resultsDictionary:executionDescriptor:]
iris_gpu_linear_bf16_mpsgraph_into (iris_metal.m:5691)
iris_gpu_linear_bf16_native_into (iris_metal.m:5799)
single_block_forward_bf16 (iris_transformer_flux.c:2939)
iris_transformer_forward_bf16_flux (iris_transformer_flux.c:3217)
iris_transformer_forward_refs_flux (iris_transformer_flux.c:4040)
iris_sample_euler_refs_flux (iris_sample.c:525)
iris_img2img (iris.c:1329)

Root cause

The MPSGraph linear caches (g_linear_graph_cache_bf16 and g_linear_graph_cache, both 32 entries) use a slot-0-overwrite eviction policy. When the cache is full and a new (seq_len, in_dim, out_dim) tuple is needed, slot 0 is always overwritten:

// get_linear_graph_cache_bf16, line 4692
int slot = 0;
if (g_linear_graph_bf16_count < MAX_LINEAR_GRAPH_CACHE) {
    slot = g_linear_graph_bf16_count++;
}

The __strong ObjC fields (MPSGraph, MPSGraphTensor, NSArray shapes) are released by ARC when overwritten. But in batch mode (iris_gpu_batch_begin/end), the entire forward pass shares a command buffer chain without committing. The previously-encoded operations still reference the freed graph objects internally.

How slot 0 thrashes within a single batch

The forward pass uses these bf16-direct cache tuples (condition: out_dim >= 8192 || seq_len >= 8192):

Per double block iteration (5 total):

  • (img_seq, 3072, 9216) for img MLP gate/up
  • (txt_seq, 3072, 9216) for txt MLP gate/up

Per single block iteration (20 total):

  • (total_seq, 3072, 27648) for fused QKV+MLP

When the cache is full from previous runs at different resolutions, all three tuples are cache misses. They cycle through slot 0 like this:

DB0: lookup (img_seq, 3072, 9216)  -> MISS -> slot 0 = graph_A
DB0: lookup (txt_seq, 3072, 9216)  -> MISS -> slot 0 = graph_B  (graph_A freed!)
DB1: lookup (img_seq, 3072, 9216)  -> MISS -> slot 0 = graph_A' (graph_B freed!)
DB1: lookup (txt_seq, 3072, 9216)  -> MISS -> slot 0 = graph_B' (graph_A' freed!)
... (repeats for 5 double blocks)
SB0: lookup (total_seq, 3072, 27648) -> MISS -> slot 0 = graph_C (graph_B'' freed!)

Each eviction releases the previous graph via ARC while the command buffer still has pending encodes that reference it. MPSGraph's internal logging (MPSGraphOSLog, top of the crash stack) accesses the freed graph, dereferences a zeroed pointer at offset 8, and crashes.

Why img2img triggers it

Each different image resolution creates unique (seq_len, in_dim, out_dim) tuples. img2img with reference images changes the effective img_seq to combined_img_seq (= target + reference tokens), producing new tuples that differ from regular txt2img. Users trying different reference image sizes fill the 32-entry cache quickly (3 bf16-direct entries per resolution, overflow after ~11 different resolutions). Once the cache is full, the next generation with a new resolution triggers the thrashing.

The same issue exists in g_linear_graph_cache (fp32-cast path, also 32 entries) and g_sdpa_graph_cache (attention path, only 8 entries).

Suggested fix

Before evicting a cache entry, flush pending GPU work so the old graph objects are no longer referenced:

int slot = 0;
if (g_linear_graph_bf16_count < MAX_LINEAR_GRAPH_CACHE) {
    slot = g_linear_graph_bf16_count++;
} else {
    // Flush pending GPU work before evicting to avoid use-after-free
    if (g_tensor_batch_mode && g_tensor_cmd) {
        if (g_tensor_cmd.status < MTLCommandBufferStatusCommitted) {
            [g_tensor_cmd commit];
        }
        [g_tensor_cmd waitUntilCompleted];
        g_tensor_cmd = [g_queue commandBuffer];
    }
}

Or increase MAX_LINEAR_GRAPH_CACHE to accommodate more resolutions (e.g., 128 or 256).

Or retain evicted graph objects in a deferred-release list (similar to pool_flush_deferred for tensor buffers) that is only flushed at iris_gpu_batch_end.

Lingua principale
C
Stelle
2k
Fork
152
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di antirez/iris.c

Tutte le issue di antirez/iris.c

Issue simili

Altre issue su C

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.