DF-1231 / fix.diff
diff --git a/sys/dev/raid/aac/aac.c b/sys/dev/raid/aac/aac.c --- a/sys/dev/raid/aac/aac.c +++ b/sys/dev/raid/aac/aac.c @@ -3525,25 +3525,39 @@ fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); if ((error = copyin(arg, &agf, sizeof(agf))) == 0) { + /* + * Serialize against aac_close_aif() which takes the same lock, + * unlinks and kfrees the ctx. Without this lock the list walk + * below races the unlink, and the tsleep race frees ctx while + * we sleep -- aac_return_aif() would then deref freed memory. + */ + lockmgr(&sc->aac_aifq_lock, LK_EXCLUSIVE); for (ctx = sc->fibctx; ctx; ctx = ctx->next) { if (agf.AdapterFibContext == ctx->unique) break; } - if (!ctx) + if (!ctx) { + lockmgr(&sc->aac_aifq_lock, LK_RELEASE); return (EFAULT); + } error = aac_return_aif(sc, ctx, agf.AifFib); if (error == EAGAIN && agf.Wait) { fwprintf(sc, HBA_FLAGS_DBG_AIF_B, "aac_getnext_aif(): waiting for AIF"); sc->aac_state |= AAC_STATE_AIF_SLEEPER; while (error == EAGAIN) { - error = tsleep(sc->aac_aifq, + /* lksleep atomically releases aac_aifq_lock while + * sleeping, so aac_close_aif() can run; it + * reacquires the lock before returning, so ctx + * cannot be freed underneath us. */ + error = lksleep(sc->aac_aifq, &sc->aac_aifq_lock, PCATCH, "aacaif", 0); if (error == 0) error = aac_return_aif(sc, ctx, agf.AifFib); } sc->aac_state &= ~AAC_STATE_AIF_SLEEPER; } + lockmgr(&sc->aac_aifq_lock, LK_RELEASE); } return(error); } |