[FFmpeg-devel] [PATCH 1/4] avformat/mpegenc: Ensure packet queue stays valid
Andreas Rheinhardt
andreas.rheinhardt at gmail.com
Thu Feb 18 14:49:11 EET 2021
Andreas Rheinhardt:
> The MPEG-PS muxer uses a custom queue of custom packets. To keep track
> of it, it has a pointer (named predecode_packet) to the head of the
> queue and a pointer to where the next packet is to be added (it points
> to the next-pointer of the last element of the queue); furthermore,
> there is also a pointer that points into the queue (called premux_packet).
>
> The exact behaviour was as follows: If premux_packet was NULL when a
> packet is received, it is taken to mean that the old queue is empty and
> a new queue is started. premux_packet will point to the head of said
> queue and the next_packet-pointer points to its next pointer. If
> predecode_packet is NULL, it will also made to point to the newly
> allocated element.
>
> But if premux_packet is NULL and predecode_packet is not, then there
> will be two queues with head elements premux_packet and
> predecode_packet. Yet only elements reachable from predecode_packet are
> ever freed, so the premux_packet queue leaks.
> Worse yet, when the predecode_packet queue will be eventually exhausted,
> predecode_packet will be made to point into the other queue and when
> predecode_packet will be freed, the next pointer of the preceding
> element of the queue will still point to the element just freed. This
> element might very well be still reachable from premux_packet which
> leads to use-after-frees lateron. This happened in the tickets mentioned
> below.
>
> Fix this by never creating two queues in the first place by checking for
> predecode_packet to know whether the queue is empty. If premux_packet is
> NULL, then it is set to the newly allocated element of the queue.
>
> Fixes tickets #6887, #8188 and #8266.
>
> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at gmail.com>
> ---
> Disclaimer: I don't know MPEG program streams very well; it might very
> well be that the mere fact that premux_packet can be NULL while
> predecode_packet isn't is indicative of a deeper bug. All I know is that
> this patch only changes behaviour in case the old behaviour was broken
> (i.e. led to leaks or use-after-frees).
>
> libavformat/mpegenc.c | 19 +++++++++++--------
> 1 file changed, 11 insertions(+), 8 deletions(-)
>
> diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c
> index 9bd0a555d4..810dd717ca 100644
> --- a/libavformat/mpegenc.c
> +++ b/libavformat/mpegenc.c
> @@ -48,9 +48,9 @@ typedef struct StreamInfo {
> uint8_t id;
> int max_buffer_size; /* in bytes */
> int buffer_index;
> - PacketDesc *predecode_packet;
> + PacketDesc *predecode_packet; /* start of packet queue */
> + PacketDesc *last_packet; /* end of packet queue */
> PacketDesc *premux_packet;
> - PacketDesc **next_packet;
> int packet_number;
> uint8_t lpcm_header[3];
> int lpcm_align;
> @@ -986,6 +986,8 @@ static int remove_decoded_packets(AVFormatContext *ctx, int64_t scr)
> }
> stream->buffer_index -= pkt_desc->size;
> stream->predecode_packet = pkt_desc->next;
> + if (!stream->predecode_packet)
> + stream->last_packet = NULL;
> av_freep(&pkt_desc);
> }
> }
> @@ -1177,12 +1179,16 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt)
> av_log(ctx, AV_LOG_TRACE, "dts:%f pts:%f flags:%d stream:%d nopts:%d\n",
> dts / 90000.0, pts / 90000.0, pkt->flags,
> pkt->stream_index, pts != AV_NOPTS_VALUE);
> - if (!stream->premux_packet)
> - stream->next_packet = &stream->premux_packet;
> - *stream->next_packet =
> pkt_desc = av_mallocz(sizeof(PacketDesc));
> if (!pkt_desc)
> return AVERROR(ENOMEM);
> + if (!stream->predecode_packet) {
> + stream->predecode_packet = pkt_desc;
> + } else
> + stream->last_packet->next = pkt_desc;
> + stream->last_packet = pkt_desc;
> + if (!stream->premux_packet)
> + stream->premux_packet = pkt_desc;
> pkt_desc->pts = pts;
> pkt_desc->dts = dts;
>
> @@ -1200,9 +1206,6 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt)
>
> pkt_desc->unwritten_size =
> pkt_desc->size = size;
> - if (!stream->predecode_packet)
> - stream->predecode_packet = pkt_desc;
> - stream->next_packet = &pkt_desc->next;
>
> if (av_fifo_realloc2(stream->fifo, av_fifo_size(stream->fifo) + size) < 0)
> return -1;
>
Will apply this patchset tomorrow unless there are objections.
- Andreas
More information about the ffmpeg-devel
mailing list