Fix MPCVideoDec dangling-pointer crash on resolution-changing channel switch (D3D11/DXVA2 HEVC)#1163
Conversation
CMPCVideoDecFilter::FillAVPacket() pointed AVPacket->data directly at m_pFFBuffer (a single buffer reused and possibly reallocated by every ParseInternal() call) instead of copying, for any codec that didn't need the padded buffer PRORES uses. FFmpeg's DXVA2/D3D11 HEVC hwaccel keeps that raw pointer alive well past this call: dxva2_hevc_decode_slice() stores it as ctx_pic->bitstream, and it isn't read until the frame's end_frame()/GPU submission via ff_dxva2_commit_buffer()'s memcpy(dxva_data, data, size) - i.e. after decode_slice() returns, not synchronously during it. If m_pFFBuffer gets reused/reallocated for a later packet before that GPU submission completes, the hwaccel reads through a dangling or overwritten pointer. Reproduced as a near-guaranteed crash in MPCVideoDec!ff_dxva2_commit_buffer (access violation reading the source pointer) when switching a live channel from a lower resolution to a higher one (e.g. 4K to 8K) under D3D11 hardware decoding, with both MPC Video Renderer and EVR as the downstream renderer (ruling out either renderer as the cause) - the extra latency from CD3D11Decoder::ReInitD3D11Decoder() rebuilding the decoder/surface pool for the new resolution was enough to reliably lose the race. Same-resolution channel switches never hit that reinit path and never crashed. Fixed by forcing FillAVPacket() to make a real copy (av_new_packet) whenever the filter is using DXVA2 or D3D11 hardware decoding, so each packet owns independent memory regardless of buffer reuse timing. PRORES's existing oversized-buffer copy path is unaffected.
Please provide a sample file or a link to a "live channel". |
|
Or record from live channel and upload. |
|
Uploaded repro samples here, since this needs hardware/conditions I can't ship as a small synthetic clip:
Repro steps (no TVTest/BonDriver needed, reproduces directly in MPC-BE):
Without the fix this crashes reliably (observed in |
|
A few notes on reproduction conditions, in case it doesn't trigger right away:
|
|
When you open new file - player closed current graph and all filters. For test need file with resolution changes in video stream. |
My MPC-BE crashes when playing test.ts. |
|
The patch doesn't crash. |
|
The crash has been fixed, but in a slightly different way. |
Problem
Switching a live channel from a lower resolution to a higher one (e.g. 4K to
8K) while using D3D11 (or DXVA2) hardware decoding of HEVC crashes nearly
every time, with an access violation inside
ff_dxva2_commit_buffer'smemcpy. Reproduced with both MPC Video Renderer and EVR as the downstreamrenderer, which rules out either renderer and points at MPCVideoDec.ax
itself. Same-resolution switches (4K to 4K) never crash.
Root cause
CMPCVideoDecFilter::FillAVPacket()inMPCVideoDec.cpppointedAVPacket->datadirectly atm_pFFBufferinstead of copying it, for anycodec that doesn't need PRORES's padded-buffer path.
m_pFFBufferis asingle buffer that
ParseInternal()reuses (and may reallocate viaav_fast_realloc) on every subsequent call.FFmpeg's DXVA2/D3D11 HEVC hwaccel keeps that raw pointer alive well past the
FillAVPacket()/avcodec_send_packet()call:dxva2_hevc_decode_slice()(
libavcodec/dxva2_hevc.c) stores it asctx_pic->bitstream, and it isn'tactually read until the frame's
end_frame()/GPU submission viaff_dxva2_commit_buffer()'smemcpy(dxva_data, data, size)inlibavcodec/dxva2.c— i.e. asynchronously, afterdecode_slice()alreadyreturned. If
m_pFFBuffergets reused or reallocated for a later packetbefore that GPU submission completes, the hwaccel ends up reading through a
dangling or overwritten pointer.
A resolution change adds just enough extra latency
(
CD3D11Decoder::ReInitD3D11Decoder()tearing down and rebuilding thedecoder/surface pool) for this race to be won reliably. Same-resolution
switches never hit that reinit path, so the race window stays narrow enough
to not normally reproduce — but the underlying bug is not actually specific
to resolution changes.
Fix
Force
FillAVPacket()to make a realav_new_packetcopy whenever thefilter is using DXVA2 or D3D11 hardware decoding, instead of aliasing the
shared
bufferpointer, so each packet owns independent memory regardlessof how/when
m_pFFBuffergets reused. The existing PRORES oversized-buffercopy-path behavior (
pkt->sizeleft as the paddedsize) is unchanged.Testing
Repeated 4K -> 8K -> 4K -> 8K ... channel switching under D3D11 hardware
decoding (HEVC), with both MPC Video Renderer and EVR — previously crashed
on almost every switch to the higher resolution, no crashes after this fix
across many repeated switches.