fix(host/vaapi): plug two AVBufferRef leaks in DmabufInner::open
Surfaced while writing the unsafe-soundness proofs (2/N): both are refcount leaks (sound — never dangling/double-free — so the SAFETY proofs held, but real bugs on the persistent punktfunk1-host listener that opens a fresh encoder per session). 1. Per-session leak: `par->hw_frames_ctx = av_buffer_ref(drm_frames)` created a second owned ref. `av_buffersrc_parameters_set` takes its OWN ref of `par->hw_frames_ctx`, and `av_free(par)` frees only the struct, not the ref — so the extra ref leaked every session, pinning the DRM frames ctx + device. Fix: assign `drm_frames` borrowed (the standard ffmpeg pattern); our single owned ref lives in DmabufInner and is unref'd in Drop. 2. Error-path leak: the final `open_vaapi_encoder(...)?` returned without the unref ladder every other error path runs, leaking graph/drm_frames/ vaapi_device/drm_device on encoder-open failure. Fix: match + clean up before returning (nv12_ctx is borrowed from the sink → freed by graph teardown). cargo clippy -p punktfunk-host --all-targets -- -D warnings clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -596,12 +596,12 @@ impl DmabufInner {
|
|||||||
num: 1,
|
num: 1,
|
||||||
den: fps as c_int,
|
den: fps as c_int,
|
||||||
};
|
};
|
||||||
// NOTE(leak, not UB): `av_buffersrc_parameters_set` takes its OWN ref of
|
// Assign `drm_frames` BORROWED (no extra ref): `av_buffersrc_parameters_set` takes its
|
||||||
// `par->hw_frames_ctx`, and `av_free(par)` frees only the struct (not the ref). So this
|
// own ref of `par->hw_frames_ctx` (via av_buffer_replace), and `av_free(par)` frees only
|
||||||
// `av_buffer_ref(drm_frames)` ref is leaked once per session — assigning `drm_frames`
|
// the struct, not the ref. Our single owned `drm_frames` ref is retained, lives in
|
||||||
// borrowed (no extra ref) is the correct ffmpeg pattern. Sound (a refcount leak, never a
|
// `DmabufInner`, and is unref'd in `Drop`. Wrapping it in `av_buffer_ref` here would leak
|
||||||
// dangling/double-free), but it keeps the DRM frames ctx + device alive past `Drop`.
|
// that extra ref every session (the persistent listener would accumulate them).
|
||||||
(*par).hw_frames_ctx = ffi::av_buffer_ref(drm_frames);
|
(*par).hw_frames_ctx = drm_frames;
|
||||||
let r = ffi::av_buffersrc_parameters_set(src, par);
|
let r = ffi::av_buffersrc_parameters_set(src, par);
|
||||||
ffi::av_free(par as *mut _);
|
ffi::av_free(par as *mut _);
|
||||||
if r < 0 {
|
if r < 0 {
|
||||||
@@ -656,10 +656,12 @@ impl DmabufInner {
|
|||||||
ffi::av_buffer_unref(&mut drm_device);
|
ffi::av_buffer_unref(&mut drm_device);
|
||||||
bail!("filter sink has no VAAPI frames context");
|
bail!("filter sink has no VAAPI frames context");
|
||||||
}
|
}
|
||||||
// NOTE(leak, not UB): unlike the error paths above, this `?` returns without unref'ing
|
// On encoder-open failure, free the graph + our owned buffer refs before bailing (matching
|
||||||
// `graph`/`drm_frames`/`vaapi_device`/`drm_device` — so an encoder-open failure leaks them.
|
// every error path above) so a failed session doesn't leak them. `nv12_ctx` is borrowed
|
||||||
// Sound (leak only); only the unhappy path, when the session is failing anyway.
|
// from the sink (owned by `graph`), so `avfilter_graph_free` reclaims it — don't unref it
|
||||||
let enc = open_vaapi_encoder(
|
// separately. On success the encoder takes its own ref of `vaapi_device`, and `drm_frames`/
|
||||||
|
// `vaapi_device`/`drm_device`/`graph` move into `DmabufInner` (freed in `Drop`).
|
||||||
|
let enc = match open_vaapi_encoder(
|
||||||
codec,
|
codec,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -667,7 +669,16 @@ impl DmabufInner {
|
|||||||
bitrate_bps,
|
bitrate_bps,
|
||||||
vaapi_device,
|
vaapi_device,
|
||||||
nv12_ctx,
|
nv12_ctx,
|
||||||
)?;
|
) {
|
||||||
|
Ok(enc) => enc,
|
||||||
|
Err(e) => {
|
||||||
|
ffi::avfilter_graph_free(&mut graph);
|
||||||
|
ffi::av_buffer_unref(&mut drm_frames);
|
||||||
|
ffi::av_buffer_unref(&mut vaapi_device);
|
||||||
|
ffi::av_buffer_unref(&mut drm_device);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
encoder = codec.vaapi_name(),
|
encoder = codec.vaapi_name(),
|
||||||
|
|||||||
Reference in New Issue
Block a user