Commit 972fe1ccd3 for ffmpeg

commit 972fe1ccd326538fe9a6b5d5094b7880b5443429
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Thu Oct 29 00:00:23 2026 +0100

    avcodec/flac_parser: score the header chain without recursion

    Fixes: iEdHjyvZ3jOg
    Fixes: AISLE-2026-0150-00454
    Fixes: stack overflow
    Stack overflow Replicated through API with a 512 KB stack limit

    Found-by: Joshua Rogers <joshua.rogers@aisle.com>

diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c
index 9a5f5fd03a..f50b0b262d 100644
--- a/libavcodec/flac_parser.c
+++ b/libavcodec/flac_parser.c
@@ -75,6 +75,8 @@ typedef struct FLACHeaderMarker {
     struct FLACHeaderMarker *next;       /**< next CRC-8 verified header that
                                               immediately follows this one in
                                               the bytestream                  */
+    struct FLACHeaderMarker *prev;       /**< CRC-8 verified header that
+                                              immediately precedes this one  */
     struct FLACHeaderMarker *best_child; /**< following frame header with
                                               which this frame has the best
                                               score with                      */
@@ -321,11 +323,12 @@ static int find_headers_search_validate(FLACParseContext *fpc, int offset)
                                      &fpc->wrap_buf,
                                      &fpc->wrap_buf_allocated_size);
     if (frame_header_is_valid(fpc->avctx, header_buf, &fi)) {
-        FLACHeaderMarker **end_handle = &fpc->headers;
+        FLACHeaderMarker **end_handle = &fpc->headers, *prev = NULL;
         int i;

         size = 0;
         while (*end_handle) {
+            prev = *end_handle;
             end_handle = &(*end_handle)->next;
             size++;
         }
@@ -338,6 +341,7 @@ static int find_headers_search_validate(FLACParseContext *fpc, int offset)
         }
         (*end_handle)->fi     = fi;
         (*end_handle)->offset = offset;
+        (*end_handle)->prev   = prev;

         for (i = 0; i < FLAC_MAX_SEQUENTIAL_HEADERS; i++)
             (*end_handle)->link_penalty[i] = FLAC_HEADER_NOT_PENALIZED_YET;
@@ -615,15 +619,18 @@ static int score_header(FLACParseContext *fpc, FLACHeaderMarker *header)

 static void score_sequences(FLACParseContext *fpc)
 {
-    FLACHeaderMarker *curr;
+    FLACHeaderMarker *curr, *last = NULL;
     int best_score = FLAC_HEADER_NOT_SCORED_YET;
     /* First pass to clear all old scores. */
-    for (curr = fpc->headers; curr; curr = curr->next)
+    for (curr = fpc->headers; curr; curr = curr->next) {
         curr->max_score = FLAC_HEADER_NOT_SCORED_YET;
+        last = curr;
+    }

-    /* Do a second pass to score them all. */
-    for (curr = fpc->headers; curr; curr = curr->next) {
-        if (score_header(fpc, curr) > best_score) {
+    /* Score from the last header backwards, so the children of each header
+       are already scored and score_header() does not recurse. */
+    for (curr = last; curr; curr = curr->prev) {
+        if (score_header(fpc, curr) >= best_score) {
             fpc->best_header = curr;
             best_score       = curr->max_score;
         }
@@ -728,6 +735,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
             curr->offset -= best_child->offset;

         best_child->offset = 0;
+        best_child->prev   = NULL;
         fpc->headers       = best_child;
         if (fpc->nb_headers_buffered >= FLAC_MIN_HEADERS) {
             fpc->best_header = best_child;
@@ -744,6 +752,8 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
             fpc->nb_headers_buffered--;
         }
         fpc->headers = fpc->best_header->next;
+        if (fpc->headers)
+            fpc->headers->prev = NULL;
         av_freep(&fpc->best_header);
         fpc->nb_headers_buffered--;
     }