[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2023-4863: Out-of-bounds write (CWE-787) in the lossless (VP8L) decoder of libwebp 1.3.1, the September 2023 zero-day. A maliciously crafted image can supply a valid but deeply unbalanced set of code lengths whose secondary lookup levels need more entries than the buffer holds, so the builder writes entries past the end of that buffer, corrupting adjacent stack or heap memory. This is reachable from any caller that decodes an attacker-supplied WebP image, and was exploited as a zero-day in the wild. Affects libwebp 1.3.1.

# Vulnerable source (src/utils/huffman_utils.c, src/dec/vp8l_dec.c, src/utils/huffman_utils.h, src/dec/vp8li_dec.h)
```c
/* ==== file: src/utils/huffman_utils.c ==== */
      if (num_open < 0) {
        return 0;
      }
      if (root_table == NULL) continue;
      for (; count[len] > 0; --count[len]) {
        HuffmanCode code;
        code.bits = (uint8_t)len;
        code.value = (uint16_t)sorted[symbol++];
        ReplicateValue(&table[key], step, table_size, code);
        key = GetNextKey(key, len);
      }
    }

    // Fill in 2nd level tables and add pointers to root table.
    for (len = root_bits + 1, step = 2; len <= MAX_ALLOWED_CODE_LENGTH;
         ++len, step <<= 1) {
      num_open <<= 1;
      num_nodes += num_open;
      num_open -= count[len];
      if (num_open < 0) {
        return 0;
      }
      if (root_table == NULL) continue;
      for (; count[len] > 0; --count[len]) {
        HuffmanCode code;
        if ((key & mask) != low) {
          table += table_size;
          table_bits = NextTableBitSize(count, len, root_bits);
          table_size = 1 << table_bits;
          total_size += table_size;
          low = key & mask;
          root_table[low].bits = (uint8_t)(table_bits + root_bits);
          root_table[low].value = (uint16_t)((table - root_table) - low);
        }
        code.bits = (uint8_t)(len - root_bits);
        code.value = (uint16_t)sorted[symbol++];
        ReplicateValue(&table[key >> root_bits], step, table_size, code);
        key = GetNextKey(key, len);
      }
    }

    // Check if tree is full.
    if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) {
      return 0;
    }
  }

  return total_size;
}

// Maximum code_lengths_size is 2328 (reached for 11-bit color_cache_bits).
// More commonly, the value is around ~280.
#define MAX_CODE_LENGTHS_SIZE \
  ((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES)
// Cut-off value for switching between heap and stack allocation.
#define SORTED_SIZE_CUTOFF 512
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
                          const int code_lengths[], int code_lengths_size) {
  int total_size;
  assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
  if (root_table == NULL) {
    total_size = BuildHuffmanTable(NULL, root_bits,
                                   code_lengths, code_lengths_size, NULL);
  } else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
    // use local stack-allocated array.
    uint16_t sorted[SORTED_SIZE_CUTOFF];
    total_size = BuildHuffmanTable(root_table, root_bits,
                                   code_lengths, code_lengths_size, sorted);
  } else {   // rare case. Use heap allocation.
    uint16_t* const sorted =
        (uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
    if (sorted == NULL) return 0;
    total_size = BuildHuffmanTable(root_table, root_bits,
                                   code_lengths, code_lengths_size, sorted);

/* ==== file: src/dec/vp8l_dec.c ==== */
      huff->bits = hcode.bits + BITS_SPECIAL_MARKER;
      huff->value = hcode.value;
    } else {
      huff->bits = 0;
      huff->value = 0;
      bits >>= AccumulateHCode(hcode, 8, huff);
      bits >>= AccumulateHCode(htree_group->htrees[RED][bits], 16, huff);
      bits >>= AccumulateHCode(htree_group->htrees[BLUE][bits], 0, huff);
      bits >>= AccumulateHCode(htree_group->htrees[ALPHA][bits], 24, huff);
      (void)bits;
    }
  }
}

static int ReadHuffmanCodeLengths(
    VP8LDecoder* const dec, const int* const code_length_code_lengths,
    int num_symbols, int* const code_lengths) {
  int ok = 0;
  VP8LBitReader* const br = &dec->br_;
  int symbol;
  int max_symbol;
  int prev_code_len = DEFAULT_CODE_LENGTH;
  HuffmanCode table[1 << LENGTHS_TABLE_BITS];

  if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
                             code_length_code_lengths,
                             NUM_CODE_LENGTH_CODES)) {
    goto End;
  }

  if (VP8LReadBits(br, 1)) {    // use length
    const int length_nbits = 2 + 2 * VP8LReadBits(br, 3);
    max_symbol = 2 + VP8LReadBits(br, length_nbits);
    if (max_symbol > num_symbols) {
      goto End;
    }
  } else {
    max_symbol = num_symbols;
  }

  symbol = 0;
  while (symbol < num_symbols) {
    const HuffmanCode* p;
    int code_len;
    if (max_symbol-- == 0) break;
    VP8LFillBitWindow(br);
    p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
    VP8LSetBitPos(br, br->bit_pos_ + p->bits);
    code_len = p->value;
    if (code_len < kCodeLengthLiterals) {
      code_lengths[symbol++] = code_len;
      if (code_len != 0) prev_code_len = code_len;
    } else {
      const int use_prev = (code_len == kCodeLengthRepeatCode);
      const int slot = code_len - kCodeLengthLiterals;
      const int extra_bits = kCodeLengthExtraBits[slot];
      const int repeat_offset = kCodeLengthRepeatOffsets[slot];
      int repeat = VP8LReadBits(br, extra_bits) + repeat_offset;
      if (symbol + repeat > num_symbols) {
        goto End;
      } else {
        const int length = use_prev ? prev_code_len : 0;
        while (repeat-- > 0) code_lengths[symbol++] = length;
      }
    }
  }
  ok = 1;

 End:
  if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
  return ok;
}

// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman
// tree.
static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
                           int* const code_lengths, HuffmanCode* const table) {
  int ok = 0;
  int size = 0;
  VP8LBitReader* const br = &dec->br_;
  const int simple_code = VP8LReadBits(br, 1);

  memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths));

  if (simple_code) {  // Read symbols, codes & code lengths directly.
    const int num_symbols = VP8LReadBits(br, 1) + 1;
    const int first_symbol_len_code = VP8LReadBits(br, 1);
    // The first code is either 1 bit or 8 bit code.
    int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8);
    code_lengths[symbol] = 1;
    // The second code (if present), is always 8 bits long.
    if (num_symbols == 2) {
      symbol = VP8LReadBits(br, 8);
      code_lengths[symbol] = 1;
        /* ... */
                                code_lengths);
  }

  ok = ok && !br->eos_;
  if (ok) {
    size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS,
                                 code_lengths, alphabet_size);
  }
  if (!ok || size == 0) {
    dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
    return 0;
  }
  return size;
}

static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
                            int color_cache_bits, int allow_recursion) {
  int i, j;
  VP8LBitReader* const br = &dec->br_;
  VP8LMetadata* const hdr = &dec->hdr_;
  uint32_t* huffman_image = NULL;
  HTreeGroup* htree_groups = NULL;
  HuffmanCode* huffman_tables = NULL;
  HuffmanCode* huffman_table = NULL;
  int num_htree_groups = 1;
  int num_htree_groups_max = 1;
  int max_alphabet_size = 0;
  int* code_lengths = NULL;
  const int table_size = kTableSize[color_cache_bits];
  int* mapping = NULL;
  int ok = 0;

  if (allow_recursion && VP8LReadBits(br, 1)) {
    // use meta Huffman codes.
    const int huffman_precision = VP8LReadBits(br, 3) + 2;
    const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision);
    const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision);
    const int huffman_pixs = huffman_xsize * huffman_ysize;
    if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec,
                           &huffman_image)) {
      goto Error;
    }
    hdr->huffman_subsample_bits_ = huffman_precision;
    for (i = 0; i < huffman_pixs; ++i) {
      // The huffman data is stored in red and green bytes.
      const int group = (huffman_image[i] >> 8) & 0xffff;
      huffman_image[i] = group;
      if (group >= num_htree_groups_max) {
        num_htree_groups_max = group + 1;
      }
        /* ... */
        huffman_image[i] = *mapped_group;
      }
    } else {
      num_htree_groups = num_htree_groups_max;
    }
  }

  if (br->eos_) goto Error;

  // Find maximum alphabet size for the htree group.
  for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
    int alphabet_size = kAlphabetSize[j];
    if (j == 0 && color_cache_bits > 0) {
      alphabet_size += 1 << color_cache_bits;
    }
    if (max_alphabet_size < alphabet_size) {
      max_alphabet_size = alphabet_size;
    }
  }

  code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
                                      sizeof(*code_lengths));
  huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size,
                                                sizeof(*huffman_tables));
  htree_groups = VP8LHtreeGroupsNew(num_htree_groups);

  if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) {
    dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
    goto Error;
  }

  huffman_table = huffman_tables;
  for (i = 0; i < num_htree_groups_max; ++i) {
    // If the index "i" is unused in the Huffman image, just make sure the
    // coefficients are valid but do not store them.
    if (mapping != NULL && mapping[i] == -1) {
      for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
        int alphabet_size = kAlphabetSize[j];
        if (j == 0 && color_cache_bits > 0) {
          alphabet_size += (1 << color_cache_bits);
        }
        // Passing in NULL so that nothing gets filled.
        if (!ReadHuffmanCode(alphabet_size, dec, code_lengths, NULL)) {
          goto Error;
        }
      }
    } else {
      HTreeGroup* const htree_group =
          &htree_groups[(mapping == NULL) ? i : mapping[i]];
      HuffmanCode** const htrees = htree_group->htrees;
      int size;
      int total_size = 0;
      int is_trivial_literal = 1;
      int max_bits = 0;
      for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
        int alphabet_size = kAlphabetSize[j];
        htrees[j] = huffman_table;
        if (j == 0 && color_cache_bits > 0) {
          alphabet_size += (1 << color_cache_bits);
        }
        size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table);
        if (size == 0) {
          goto Error;
        }
        if (is_trivial_literal && kLiteralMap[j] == 1) {
          is_trivial_literal = (huffman_table->bits == 0);
        }
        total_size += huffman_table->bits;
        huffman_table += size;
        if (j <= ALPHA) {
          int local_max_bits = code_lengths[0];
          int k;
          for (k = 1; k < alphabet_size; ++k) {
            if (code_lengths[k] > local_max_bits) {
        /* ... */
      htree_group->is_trivial_code = 0;
      if (is_trivial_literal) {
        const int red = htrees[RED][0].value;
        const int blue = htrees[BLUE][0].value;
        const int alpha = htrees[ALPHA][0].value;
        htree_group->literal_arb = ((uint32_t)alpha << 24) | (red << 16) | blue;
        if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) {
          htree_group->is_trivial_code = 1;
          htree_group->literal_arb |= htrees[GREEN][0].value << 8;
        }
      }
      htree_group->use_packed_table =
          !htree_group->is_trivial_code && (max_bits < HUFFMAN_PACKED_BITS);
      if (htree_group->use_packed_table) BuildPackedTable(htree_group);
    }
  }
  ok = 1;

  // All OK. Finalize pointers.
  hdr->huffman_image_ = huffman_image;
  hdr->num_htree_groups_ = num_htree_groups;
  hdr->htree_groups_ = htree_groups;
  hdr->huffman_tables_ = huffman_tables;

 Error:
  WebPSafeFree(code_lengths);
  WebPSafeFree(mapping);
  if (!ok) {
    WebPSafeFree(huffman_image);
    WebPSafeFree(huffman_tables);
    VP8LHtreeGroupsFree(htree_groups);
  }
  return ok;
}

//------------------------------------------------------------------------------
// Scaling.

#if !defined(WEBP_REDUCE_SIZE)
static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
        /* ... */
    case SUBTRACT_GREEN_TRANSFORM:
      break;
    default:
      assert(0);    // can't happen
      break;
  }

  return ok;
}

// -----------------------------------------------------------------------------
// VP8LMetadata

static void InitMetadata(VP8LMetadata* const hdr) {
  assert(hdr != NULL);
  memset(hdr, 0, sizeof(*hdr));
}

static void ClearMetadata(VP8LMetadata* const hdr) {
  assert(hdr != NULL);

  WebPSafeFree(hdr->huffman_image_);
  WebPSafeFree(hdr->huffman_tables_);
  VP8LHtreeGroupsFree(hdr->htree_groups_);
  VP8LColorCacheClear(&hdr->color_cache_);
  VP8LColorCacheClear(&hdr->saved_color_cache_);
  InitMetadata(hdr);
}

// -----------------------------------------------------------------------------
// VP8LDecoder

VP8LDecoder* VP8LNew(void) {
  VP8LDecoder* const dec = (VP8LDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
  if (dec == NULL) return NULL;
  dec->status_ = VP8_STATUS_OK;
  dec->state_ = READ_DIM;

  VP8LDspInit();  // Init critical function pointers.

        /* ... */
    dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
    goto Error;
  }
  dec->state_ = READ_DIM;
  io->width = width;
  io->height = height;

  if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error;
  return 1;

 Error:
  VP8LClear(dec);
  assert(dec->status_ != VP8_STATUS_OK);
  return 0;
}

int VP8LDecodeImage(VP8LDecoder* const dec) {
  VP8Io* io = NULL;
  WebPDecParams* params = NULL;

  if (dec == NULL) return 0;

  assert(dec->hdr_.huffman_tables_ != NULL);
  assert(dec->hdr_.htree_groups_ != NULL);
  assert(dec->hdr_.num_htree_groups_ > 0);

  io = dec->io_;
  assert(io != NULL);
  params = (WebPDecParams*)io->opaque;
  assert(params != NULL);

  // Initialization.
  if (dec->state_ != READ_DATA) {
    dec->output_ = params->output;
    assert(dec->output_ != NULL);

    if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) {
      dec->status_ = VP8_STATUS_INVALID_PARAM;
      goto Err;
    }

/* ==== file: src/utils/huffman_utils.h ==== */

#define HUFFMAN_TABLE_BITS      8
#define HUFFMAN_TABLE_MASK      ((1 << HUFFMAN_TABLE_BITS) - 1)

#define LENGTHS_TABLE_BITS      7
#define LENGTHS_TABLE_MASK      ((1 << LENGTHS_TABLE_BITS) - 1)

// Huffman lookup table entry
typedef struct {
  uint8_t bits;     // number of bits used for this symbol
  uint16_t value;   // symbol value or table offset
} HuffmanCode;

// long version for holding 32b values
typedef struct {
  int bits;         // number of bits used for this symbol,
                    // or an impossible value if not a literal code.
  uint32_t value;   // 32b packed ARGB value if literal,
                    // or non-literal symbol otherwise
} HuffmanCode32;

#define HUFFMAN_PACKED_BITS 6
#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)

// Huffman table group.
// Includes special handling for the following cases:
//  - is_trivial_literal: one common literal base for RED/BLUE/ALPHA (not GREEN)
//  - is_trivial_code: only 1 code (no bit is read from bitstream)
//  - use_packed_table: few enough literal symbols, so all the bit codes
//    can fit into a small look-up table packed_table[]
// The common literal base, if applicable, is stored in 'literal_arb'.
typedef struct HTreeGroup HTreeGroup;
struct HTreeGroup {
  HuffmanCode* htrees[HUFFMAN_CODES_PER_META_CODE];
  int      is_trivial_literal;  // True, if huffman trees for Red, Blue & Alpha
                                // Symbols are trivial (have a single code).
  uint32_t literal_arb;         // If is_trivial_literal is true, this is the
                                // ARGB value of the pixel, with Green channel
                                // being set to zero.
  int is_trivial_code;          // true if is_trivial_literal with only one code
  int use_packed_table;         // use packed table below for short literal code
  // table mapping input bits to a packed values, or escape case to literal code
  HuffmanCode32 packed_table[HUFFMAN_PACKED_TABLE_SIZE];
};

// Creates the instance of HTreeGroup with specified number of tree-groups.
HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups);

// Releases the memory allocated for HTreeGroup.
void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);

// Builds Huffman lookup table assuming code lengths are in symbol order.
// The 'code_lengths' is pre-allocated temporary memory buffer used for creating
// the huffman table.
// Returns built table size or 0 in case of error (invalid tree or
// memory error).
// If root_table is NULL, it returns 0 if a lookup cannot be built, something
// > 0 otherwise (but not the table size).
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
                          const int code_lengths[], int code_lengths_size);

#ifdef __cplusplus
}    // extern "C"
#endif

#endif  // WEBP_UTILS_HUFFMAN_UTILS_H_

/* ==== file: src/dec/vp8li_dec.h ==== */
} VP8LDecodeState;

typedef struct VP8LTransform VP8LTransform;
struct VP8LTransform {
  VP8LImageTransformType type_;   // transform type.
  int                    bits_;   // subsampling bits defining transform window.
  int                    xsize_;  // transform window X index.
  int                    ysize_;  // transform window Y index.
  uint32_t*              data_;   // transform data.
};

typedef struct {
  int             color_cache_size_;
  VP8LColorCache  color_cache_;
  VP8LColorCache  saved_color_cache_;  // for incremental

  int             huffman_mask_;
  int             huffman_subsample_bits_;
  int             huffman_xsize_;
  uint32_t*       huffman_image_;
  int             num_htree_groups_;
  HTreeGroup*     htree_groups_;
  HuffmanCode*    huffman_tables_;
} VP8LMetadata;

typedef struct VP8LDecoder VP8LDecoder;
struct VP8LDecoder {
  VP8StatusCode    status_;
  VP8LDecodeState  state_;
  VP8Io*           io_;

  const WebPDecBuffer* output_;    // shortcut to io->opaque->output

  uint32_t*        pixels_;        // Internal data: either uint8_t* for alpha
                                   // or uint32_t* for BGRA.
  uint32_t*        argb_cache_;    // Scratch buffer for temporary BGRA storage.

  VP8LBitReader    br_;
  int              incremental_;   // if true, incremental decoding is expected
  VP8LBitReader    saved_br_;      // note: could be local variables too
```

[Assistant]
diff --git a/src/dec/vp8l_dec.c b/src/dec/vp8l_dec.c
--- a/src/dec/vp8l_dec.c
+++ b/src/dec/vp8l_dec.c
@@ -253,11 +253,11 @@ static int ReadHuffmanCodeLengths(
   int symbol;
   int max_symbol;
   int prev_code_len = DEFAULT_CODE_LENGTH;
-  HuffmanCode table[1 << LENGTHS_TABLE_BITS];
+  HuffmanTables tables;

-  if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
-                             code_length_code_lengths,
-                             NUM_CODE_LENGTH_CODES)) {
+  if (!VP8LHuffmanTablesAllocate(1 << LENGTHS_TABLE_BITS, &tables) ||
+      !VP8LBuildHuffmanTable(&tables, LENGTHS_TABLE_BITS,
+                             code_length_code_lengths, NUM_CODE_LENGTH_CODES)) {
     goto End;
   }

@@ -277,7 +277,7 @@ static int ReadHuffmanCodeLengths(
     int code_len;
     if (max_symbol-- == 0) break;
     VP8LFillBitWindow(br);
-    p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
+    p = &tables.curr_segment->start[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
     VP8LSetBitPos(br, br->bit_pos_ + p->bits);
     code_len = p->value;
     if (code_len < kCodeLengthLiterals) {
@@ -300,6 +300,7 @@ static int ReadHuffmanCodeLengths(
   ok = 1;

  End:
+  VP8LHuffmanTablesDeallocate(&tables);
   if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
   return ok;
 }
@@ -307,7 +308,8 @@ static int ReadHuffmanCodeLengths(
 // 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman
 // tree.
 static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
-                           int* const code_lengths, HuffmanCode* const table) {
+                           int* const code_lengths,
+                           HuffmanTables* const table) {
   int ok = 0;
   int size = 0;
   VP8LBitReader* const br = &dec->br_;
@@ -362,8 +364,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
   VP8LMetadata* const hdr = &dec->hdr_;
   uint32_t* huffman_image = NULL;
   HTreeGroup* htree_groups = NULL;
-  HuffmanCode* huffman_tables = NULL;
-  HuffmanCode* huffman_table = NULL;
+  HuffmanTables* huffman_tables = &hdr->huffman_tables_;
   int num_htree_groups = 1;
   int num_htree_groups_max = 1;
   int max_alphabet_size = 0;
@@ -372,6 +373,10 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
   int* mapping = NULL;
   int ok = 0;

+  // Check the table has been 0 initialized (through InitMetadata).
+  assert(huffman_tables->root.start == NULL);
+  assert(huffman_tables->curr_segment == NULL);
+
   if (allow_recursion && VP8LReadBits(br, 1)) {
     // use meta Huffman codes.
     const int huffman_precision = VP8LReadBits(br, 3) + 2;
@@ -434,16 +439,15 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,

   code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
                                       sizeof(*code_lengths));
-  huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size,
-                                                sizeof(*huffman_tables));
   htree_groups = VP8LHtreeGroupsNew(num_htree_groups);

-  if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) {
+  if (htree_groups == NULL || code_lengths == NULL ||
+      !VP8LHuffmanTablesAllocate(num_htree_groups * table_size,
+                                 huffman_tables)) {
     dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
     goto Error;
   }

-  huffman_table = huffman_tables;
   for (i = 0; i < num_htree_groups_max; ++i) {
     // If the index "i" is unused in the Huffman image, just make sure the
     // coefficients are valid but do not store them.
@@ -468,19 +472,20 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
       int max_bits = 0;
       for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
         int alphabet_size = kAlphabetSize[j];
-        htrees[j] = huffman_table;
         if (j == 0 && color_cache_bits > 0) {
           alphabet_size += (1 << color_cache_bits);
         }
-        size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table);
+        size =
+            ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_tables);
+        htrees[j] = huffman_tables->curr_segment->curr_table;
         if (size == 0) {
           goto Error;
         }
         if (is_trivial_literal && kLiteralMap[j] == 1) {
-          is_trivial_literal = (huffman_table->bits == 0);
+          is_trivial_literal = (htrees[j]->bits == 0);
         }
-        total_size += huffman_table->bits;
-        huffman_table += size;
+        total_size += htrees[j]->bits;
+        huffman_tables->curr_segment->curr_table += size;
         if (j <= ALPHA) {
           int local_max_bits = code_lengths[0];
           int k;
@@ -515,14 +520,13 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
   hdr->huffman_image_ = huffman_image;
   hdr->num_htree_groups_ = num_htree_groups;
   hdr->htree_groups_ = htree_groups;
-  hdr->huffman_tables_ = huffman_tables;

  Error:
   WebPSafeFree(code_lengths);
   WebPSafeFree(mapping);
   if (!ok) {
     WebPSafeFree(huffman_image);
-    WebPSafeFree(huffman_tables);
+    VP8LHuffmanTablesDeallocate(huffman_tables);
     VP8LHtreeGroupsFree(htree_groups);
   }
   return ok;
@@ -1358,7 +1362,7 @@ static void ClearMetadata(VP8LMetadata* const hdr) {
   assert(hdr != NULL);

   WebPSafeFree(hdr->huffman_image_);
-  WebPSafeFree(hdr->huffman_tables_);
+  VP8LHuffmanTablesDeallocate(&hdr->huffman_tables_);
   VP8LHtreeGroupsFree(hdr->htree_groups_);
   VP8LColorCacheClear(&hdr->color_cache_);
   VP8LColorCacheClear(&hdr->saved_color_cache_);
@@ -1673,7 +1677,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {

   if (dec == NULL) return 0;

-  assert(dec->hdr_.huffman_tables_ != NULL);
+  assert(dec->hdr_.huffman_tables_.root.start != NULL);
   assert(dec->hdr_.htree_groups_ != NULL);
   assert(dec->hdr_.num_htree_groups_ > 0);

diff --git a/src/dec/vp8li_dec.h b/src/dec/vp8li_dec.h
--- a/src/dec/vp8li_dec.h
+++ b/src/dec/vp8li_dec.h
@@ -51,7 +51,7 @@ typedef struct {
   uint32_t*       huffman_image_;
   int             num_htree_groups_;
   HTreeGroup*     htree_groups_;
-  HuffmanCode*    huffman_tables_;
+  HuffmanTables   huffman_tables_;
 } VP8LMetadata;

 typedef struct VP8LDecoder VP8LDecoder;
diff --git a/src/utils/huffman_utils.c b/src/utils/huffman_utils.c
--- a/src/utils/huffman_utils.c
+++ b/src/utils/huffman_utils.c
@@ -177,21 +177,24 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
       if (num_open < 0) {
         return 0;
       }
-      if (root_table == NULL) continue;
       for (; count[len] > 0; --count[len]) {
         HuffmanCode code;
         if ((key & mask) != low) {
-          table += table_size;
+          if (root_table != NULL) table += table_size;
           table_bits = NextTableBitSize(count, len, root_bits);
           table_size = 1 << table_bits;
           total_size += table_size;
           low = key & mask;
-          root_table[low].bits = (uint8_t)(table_bits + root_bits);
-          root_table[low].value = (uint16_t)((table - root_table) - low);
+          if (root_table != NULL) {
+            root_table[low].bits = (uint8_t)(table_bits + root_bits);
+            root_table[low].value = (uint16_t)((table - root_table) - low);
+          }
+        }
+        if (root_table != NULL) {
+          code.bits = (uint8_t)(len - root_bits);
+          code.value = (uint16_t)sorted[symbol++];
+          ReplicateValue(&table[key >> root_bits], step, table_size, code);
         }
-        code.bits = (uint8_t)(len - root_bits);
-        code.value = (uint16_t)sorted[symbol++];
-        ReplicateValue(&table[key >> root_bits], step, table_size, code);
         key = GetNextKey(key, len);
       }
     }
@@ -211,25 +214,83 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
   ((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES)
 // Cut-off value for switching between heap and stack allocation.
 #define SORTED_SIZE_CUTOFF 512
-int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
+int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
                           const int code_lengths[], int code_lengths_size) {
-  int total_size;
+  const int total_size =
+      BuildHuffmanTable(NULL, root_bits, code_lengths, code_lengths_size, NULL);
   assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
-  if (root_table == NULL) {
-    total_size = BuildHuffmanTable(NULL, root_bits,
-                                   code_lengths, code_lengths_size, NULL);
-  } else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
+  if (total_size == 0 || root_table == NULL) return total_size;
+
+  if (root_table->curr_segment->curr_table + total_size >=
+      root_table->curr_segment->start + root_table->curr_segment->size) {
+    // If 'root_table' does not have enough memory, allocate a new segment.
+    // The available part of root_table->curr_segment is left unused because we
+    // need a contiguous buffer.
+    const int segment_size = root_table->curr_segment->size;
+    struct HuffmanTablesSegment* next =
+        (HuffmanTablesSegment*)WebPSafeMalloc(1, sizeof(*next));
+    if (next == NULL) return 0;
+    // Fill the new segment.
+    // We need at least 'total_size' but if that value is small, it is better to
+    // allocate a big chunk to prevent more allocations later. 'segment_size' is
+    // therefore chosen (any other arbitrary value could be chosen).
+    next->size = total_size > segment_size ? total_size : segment_size;
+    next->start =
+        (HuffmanCode*)WebPSafeMalloc(next->size, sizeof(*next->start));
+    if (next->start == NULL) {
+      WebPSafeFree(next);
+      return 0;
+    }
+    next->curr_table = next->start;
+    next->next = NULL;
+    // Point to the new segment.
+    root_table->curr_segment->next = next;
+    root_table->curr_segment = next;
+  }
+  if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
     // use local stack-allocated array.
     uint16_t sorted[SORTED_SIZE_CUTOFF];
-    total_size = BuildHuffmanTable(root_table, root_bits,
-                                   code_lengths, code_lengths_size, sorted);
-  } else {   // rare case. Use heap allocation.
+    BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
+                      code_lengths, code_lengths_size, sorted);
+  } else {  // rare case. Use heap allocation.
     uint16_t* const sorted =
         (uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
     if (sorted == NULL) return 0;
-    total_size = BuildHuffmanTable(root_table, root_bits,
-                                   code_lengths, code_lengths_size, sorted);
+    BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
+                      code_lengths, code_lengths_size, sorted);
     WebPSafeFree(sorted);
   }
   return total_size;
 }
+
+int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables) {
+  // Have 'segment' point to the first segment for now, 'root'.
+  HuffmanTablesSegment* const root = &huffman_tables->root;
+  huffman_tables->curr_segment = root;
+  // Allocate root.
+  root->start = (HuffmanCode*)WebPSafeMalloc(size, sizeof(*root->start));
+  if (root->start == NULL) return 0;
+  root->curr_table = root->start;
+  root->next = NULL;
+  root->size = size;
+  return 1;
+}
+
+void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables) {
+  HuffmanTablesSegment *current, *next;
+  if (huffman_tables == NULL) return;
+  // Free the root node.
+  current = &huffman_tables->root;
+  next = current->next;
+  WebPSafeFree(current->start);
+  current->start = NULL;
+  current->next = NULL;
+  current = next;
+  // Free the following nodes.
+  while (current != NULL) {
+    next = current->next;
+    WebPSafeFree(current->start);
+    WebPSafeFree(current);
+    current = next;
+  }
+}
diff --git a/src/utils/huffman_utils.h b/src/utils/huffman_utils.h
--- a/src/utils/huffman_utils.h
+++ b/src/utils/huffman_utils.h
@@ -43,6 +43,29 @@ typedef struct {
                     // or non-literal symbol otherwise
 } HuffmanCode32;

+// Contiguous memory segment of HuffmanCodes.
+typedef struct HuffmanTablesSegment {
+  HuffmanCode* start;
+  // Pointer to where we are writing into the segment. Starts at 'start' and
+  // cannot go beyond 'start' + 'size'.
+  HuffmanCode* curr_table;
+  // Pointer to the next segment in the chain.
+  struct HuffmanTablesSegment* next;
+  int size;
+} HuffmanTablesSegment;
+
+// Chained memory segments of HuffmanCodes.
+typedef struct HuffmanTables {
+  HuffmanTablesSegment root;
+  // Currently processed segment. At first, this is 'root'.
+  HuffmanTablesSegment* curr_segment;
+} HuffmanTables;
+
+// Allocates a HuffmanTables with 'size' contiguous HuffmanCodes. Returns 0 on
+// memory allocation error, 1 otherwise.
+int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables);
+void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables);
+
 #define HUFFMAN_PACKED_BITS 6
 #define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)

@@ -78,9 +101,7 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
 // the huffman table.
 // Returns built table size or 0 in case of error (invalid tree or
 // memory error).
-// If root_table is NULL, it returns 0 if a lookup cannot be built, something
-// > 0 otherwise (but not the table size).
-int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
+int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
                           const int code_lengths[], int code_lengths_size);

 #ifdef __cplusplus


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2023-49502: At setup bwdif checked a frame overall width and height (requiring at least 3 wide and 4 tall) but not the size of the smaller chroma-subsampled component buffers. For a subsampled pixel layout such as yuv420p the chroma components are half size, so a frame whose overall width and height satisfy the check can still carry a chroma component smaller than the interpolation kernel; bwdif accepted such a frame and read beyond the edges of that undersized chroma component while deinterlacing. Reachable from an attacker-supplied clip. Affects the ffmpeg 5.1.x branch.

# Vulnerable source (libavfilter/vf_bwdif.c)
```c
    AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
    AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
    AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
    AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
    AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP16,
    AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16,
    AV_PIX_FMT_NONE
};

static int config_props(AVFilterLink *link)
{
    AVFilterContext *ctx = link->src;
    BWDIFContext *s = link->src->priv;
    YADIFContext *yadif = &s->yadif;

    link->time_base = av_mul_q(ctx->inputs[0]->time_base, (AVRational){1, 2});
    link->w         = link->src->inputs[0]->w;
    link->h         = link->src->inputs[0]->h;

    if(yadif->mode&1)
        link->frame_rate = av_mul_q(link->src->inputs[0]->frame_rate, (AVRational){2,1});

    if (link->w < 3 || link->h < 4) {
        av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or 4 lines is not supported\n");
        return AVERROR(EINVAL);
    }

    yadif->csp = av_pix_fmt_desc_get(link->format);
    yadif->filter = filter;
    if (yadif->csp->comp[0].depth > 8) {
        s->filter_intra = filter_intra_16bit;
        s->filter_line  = filter_line_c_16bit;
        s->filter_edge  = filter_edge_16bit;
    } else {
        s->filter_intra = filter_intra;
        s->filter_line  = filter_line_c;
        s->filter_edge  = filter_edge;
    }

#if ARCH_X86
```

[Assistant]
diff --git a/libavfilter/vf_bwdif.c b/libavfilter/vf_bwdif.c
--- a/libavfilter/vf_bwdif.c
+++ b/libavfilter/vf_bwdif.c
@@ -333,13 +333,14 @@ static int config_props(AVFilterLink *link)
     if(yadif->mode&1)
         link->frame_rate = av_mul_q(link->src->inputs[0]->frame_rate, (AVRational){2,1});

-    if (link->w < 3 || link->h < 4) {
-        av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or 4 lines is not supported\n");
+    yadif->csp = av_pix_fmt_desc_get(link->format);
+    yadif->filter = filter;
+
+    if (AV_CEIL_RSHIFT(link->w, yadif->csp->log2_chroma_w) < 3 || AV_CEIL_RSHIFT(link->h, yadif->csp->log2_chroma_h) < 4) {
+        av_log(ctx, AV_LOG_ERROR, "Video with planes less than 3 columns or 4 lines is not supported\n");
         return AVERROR(EINVAL);
     }

-    yadif->csp = av_pix_fmt_desc_get(link->format);
-    yadif->filter = filter;
     if (yadif->csp->comp[0].depth > 8) {
         s->filter_intra = filter_intra_16bit;
         s->filter_line  = filter_line_c_16bit;


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-12254: Memory-exhaustion denial of service (CWE-400 uncontrolled resource consumption) in CPython's asyncio. The _SelectorSocketTransport.writelines() method, when the socket accepts only part of the data and leaves a residual buffer, registers a write handler but does not apply the transport's write-buffer back-pressure step that the byte-oriented write() path performs. As a result the connected protocol is never told to stop producing while a slow-reading or stalled peer drains the socket, so the transport's outgoing buffer can grow without bound and exhaust process memory (gh-127655). Affects applications that send via writelines() over an asyncio selector transport. nixpkgs ships an affected python3 (3.12.8).

# Vulnerable source (Lib/asyncio/selector_events.py)
```python
                elif self._eof:
                    self._sock.shutdown(socket.SHUT_WR)

    def write_eof(self):
        if self._closing or self._eof:
            return
        self._eof = True
        if not self._buffer:
            self._sock.shutdown(socket.SHUT_WR)

    def writelines(self, list_of_data):
        if self._eof:
            raise RuntimeError('Cannot call writelines() after write_eof()')
        if self._empty_waiter is not None:
            raise RuntimeError('unable to writelines; sendfile is in progress')
        if not list_of_data:
            return
        self._buffer.extend([memoryview(data) for data in list_of_data])
        self._write_ready()
        # If the entire buffer couldn't be written, register a write handler
        if self._buffer:
            self._loop._add_writer(self._sock_fd, self._write_ready)

    def can_write_eof(self):
        return True

    def _call_connection_lost(self, exc):
        super()._call_connection_lost(exc)
        if self._empty_waiter is not None:
            self._empty_waiter.set_exception(
                ConnectionError("Connection is closed by peer"))

    def _make_empty_waiter(self):
        if self._empty_waiter is not None:
            raise RuntimeError("Empty waiter is already set")
        self._empty_waiter = self._loop.create_future()
        if not self._buffer:
            self._empty_waiter.set_result(None)
        return self._empty_waiter

```

[Assistant]
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -1175,6 +1175,7 @@ def writelines(self, list_of_data):
         # If the entire buffer couldn't be written, register a write handler
         if self._buffer:
             self._loop._add_writer(self._sock_fd, self._write_ready)
+            self._maybe_pause_protocol()

     def can_write_eof(self):
         return True


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-23334: This allows reading arbitrary files accessible to the server process.

# Vulnerable source (aiohttp/web_urldispatcher.py)
```python
            try:
                filepath = self._directory.joinpath(filename).resolve()
                if not self._follow_symlinks:
                    filepath.relative_to(self._directory)
            except (ValueError, FileNotFoundError):
                # ValueError for case when path point to symlink
                # with follow_symlinks is False
                return url  # relatively safe
            if filepath.is_file():
                # TODO cache file content
                # with file watcher for cache invalidation
                with filepath.open("rb") as f:
                    file_bytes = f.read()
                h = self._get_file_hash(file_bytes)
                url = url.with_query({self.VERSION_KEY: h})
                return url
        return url

    @staticmethod
    def _get_file_hash(byte_array: bytes) -> str:
        m = hashlib.sha256()  # todo sha256 can be configurable param
        m.update(byte_array)
        b64 = base64.urlsafe_b64encode(m.digest())
        return b64.decode("ascii")

    def get_info(self) -> _InfoDict:
        return {
            "directory": self._directory,
            "prefix": self._prefix,
            "routes": self._routes,
        }

    def set_options_route(self, handler: Handler) -> None:
        if "OPTIONS" in self._routes:
            raise RuntimeError("OPTIONS route was set already")
        self._routes["OPTIONS"] = ResourceRoute(
            "OPTIONS", handler, self, expect_handler=self._expect_handler
        )

    async def resolve(self, request: Request) -> _Resolve:
        /* ... */
            filepath = self._directory.joinpath(filename).resolve()
            if not self._follow_symlinks:
                filepath.relative_to(self._directory)
        except (ValueError, FileNotFoundError) as error:
            # relatively safe
            raise HTTPNotFound() from error
        except HTTPForbidden:
            raise
        except Exception as error:
            # perm error or other kind!
            request.app.logger.exception(error)
            raise HTTPNotFound() from error

        # on opening a dir, load its contents if allowed
        if filepath.is_dir():
            if self._show_index:
                try:
                    return Response(
                        text=self._directory_as_html(filepath), content_type="text/html"
                    )
                except PermissionError:
                    raise HTTPForbidden()
            else:
                raise HTTPForbidden()
        elif filepath.is_file():
            return FileResponse(filepath, chunk_size=self._chunk_size)
        else:
            raise HTTPNotFound

    def _directory_as_html(self, filepath: Path) -> str:
        # returns directory's index as html

        # sanity check
        assert filepath.is_dir()

        relative_path_to_dir = filepath.relative_to(self._directory).as_posix()
        index_of = f"Index of /{relative_path_to_dir}"
        h1 = f"<h1>{index_of}</h1>"

        index_list = []
```

[Assistant]
diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
--- a/aiohttp/web_urldispatcher.py
+++ b/aiohttp/web_urldispatcher.py
@@ -595,9 +595,14 @@
             url = url / filename

         if append_version:
+            unresolved_path = self._directory.joinpath(filename)
             try:
-                filepath = self._directory.joinpath(filename).resolve()
-                if not self._follow_symlinks:
+                if self._follow_symlinks:
+                    normalized_path = Path(os.path.normpath(unresolved_path))
+                    normalized_path.relative_to(self._directory)
+                    filepath = normalized_path.resolve()
+                else:
+                    filepath = unresolved_path.resolve()
                     filepath.relative_to(self._directory)
             except (ValueError, FileNotFoundError):
                 # ValueError for case when path point to symlink
@@ -662,8 +667,13 @@
                 # /static/\\machine_name\c$ or /static/D:\path
                 # where the static dir is totally different
                 raise HTTPForbidden()
-            filepath = self._directory.joinpath(filename).resolve()
-            if not self._follow_symlinks:
+            unresolved_path = self._directory.joinpath(filename)
+            if self._follow_symlinks:
+                normalized_path = Path(os.path.normpath(unresolved_path))
+                normalized_path.relative_to(self._directory)
+                filepath = normalized_path.resolve()
+            else:
+                filepath = unresolved_path.resolve()
                 filepath.relative_to(self._directory)
         except (ValueError, FileNotFoundError) as error:
             # relatively safe


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-33664: CWE-400 in python3Packages.python-jose 3.3.0. The jose.jwe.decrypt function accepts a compact-serialized encryption string of any length and forwards it to the internal segment-splitter before performing any validation. No upper bound on the input string is checked before deserialization begins. An attacker with access to the decryption endpoint can submit a multi-megabyte compact-form payload, causing the runtime to allocate proportional heap memory while splitting and base64-decoding each segment. The absence of an early-exit guard means resource consumption is bounded only by the caller patience and the process memory limit.

# Vulnerable source (jose/jwe.py)
```python
def decrypt(jwe_str, key):
    """Decrypts a JWE compact serialized string and returns the plaintext.

    Args:
        jwe_str (str): A JWE to be decrypt.
        key (str or dict): A key to attempt to decrypt the payload with. Can be
            individual JWK or JWK set.

    Returns:
        bytes: The plaintext bytes, assuming the authentication tag is valid.

    Raises:
        JWEError: If there is an exception verifying the token.

    Examples:
        >>> from jose import jwe
        >>> jwe.decrypt(jwe_string, 'asecret128bitkey')
        'Hello, World!'
    """
    header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str)

    # Verify that the implementation understands and can process all
    # fields that it is required to support, whether required by this
    # specification, by the algorithms being used, or by the "crit"
    # Header Parameter value, and that the values of those parameters
    # are also understood and supported.

    try:
        # Determine the Key Management Mode employed by the algorithm
        # specified by the "alg" (algorithm) Header Parameter.
        alg = header["alg"]
        enc = header["enc"]
        if alg not in ALGORITHMS.SUPPORTED:
            raise JWEError("Algorithm %s not supported." % alg)
        if enc not in ALGORITHMS.SUPPORTED:
            raise JWEError("Algorithm %s not supported." % enc)

    except KeyError:
        raise JWEParseError("alg and enc headers are required!")

```

[Assistant]
diff --git a/jose/jwe.py b/jose/jwe.py
index 2c387ff4..1e0833e7 100644
--- a/jose/jwe.py
+++ b/jose/jwe.py
@@ -76,6 +76,11 @@ def decrypt(jwe_str, key):
     >>> jwe.decrypt(jwe_string, 'asecret128bitkey')
     'Hello, World!'
     """
+
+    # limit the token size to 250 KB
+    if len(jwe_str) > 250 * 1024:
+        raise JWEError("JWE string exceeds 250 KB")
+
     header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str)


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-41671: Twisted's HTTP server failed to correctly enforce the chunk trailer size limit when incoming data arrived in multiple TCP segments, allowing a connection to feed arbitrarily large trailer data without triggering the configured guard. A separate ordering defect caused the channel to signal that all request content had been received before the final data segment was passed to the pending request, enabling an attacker to smuggle an additional pipelined HTTP request into the same connection.

# Vulnerable source (src/twisted/web/http.py)
```python
        if ext and ext.translate(None, _chunkExtChars) != b"":
            raise _MalformedChunkedDataError(
                f"Invalid characters in chunk extensions: {ext!r}."
            )

        if length == 0:
            self.state = "TRAILER"
        else:
            self.state = "BODY"

        self.length = length
        del self._buffer[0 : eolIndex + 2]
        self._start = 0
        return True

    def _dataReceived_CRLF(self) -> bool:
        """
        Await the carriage return and line feed characters that are the end of
        chunk marker that follow the chunk data.

        @returns: C{True} when the CRLF have been read, otherwise C{False}.

        @raises _MalformedChunkedDataError: when anything other than CRLF are
            received.
        """
        if len(self._buffer) < 2:
            return False

        if not self._buffer.startswith(b"\r\n"):
            raise _MalformedChunkedDataError("Chunk did not end with CRLF")

        self.state = "CHUNK_LENGTH"
        del self._buffer[0:2]
        return True

    def _dataReceived_TRAILER(self) -> bool:
        """
        Collect trailer headers if received and finish at the terminal zero-length
        chunk. Then invoke C{finishCallback} and switch to state C{'FINISHED'}.

        @returns: C{False}, as there is either insufficient data to continue,
            or no data remains.
        """
        if (
            self._receivedTrailerHeadersSize + len(self._buffer)
            > self._maxTrailerHeadersSize
        ):
            raise _MalformedChunkedDataError("Trailer headers data is too long.")

        eolIndex = self._buffer.find(b"\r\n", self._start)

        if eolIndex == -1:
            # Still no end of network line marker found.
            # Continue processing more data.
            return False

        if eolIndex > 0:
            # A trailer header was detected.
            self._trailerHeaders.append(self._buffer[0:eolIndex])
            del self._buffer[0 : eolIndex + 2]
            self._start = 0
            self._receivedTrailerHeadersSize += eolIndex + 2
            return True

        # eolIndex in this part of code is equal to 0

        data = memoryview(self._buffer)[2:].tobytes()

        del self._buffer[:]
        self.state = "FINISHED"
        self.finishCallback(data)
        return False

    def _dataReceived_BODY(self) -> bool:
        """
        Deliver any available chunk data to the C{dataCallback}. When all the
        remaining data for the chunk arrives, switch to state C{'CRLF'}.

        @returns: C{True} to continue processing of any buffered data.
        """
        if len(self._buffer) >= self.length:
            chunk = memoryview(self._buffer)[: self.length].tobytes()
            del self._buffer[: self.length]
            self.state = "CRLF"
            self.dataCallback(chunk)
        else:
            chunk = bytes(self._buffer)
            self.length -= len(chunk)
            del self._buffer[:]
            self.dataCallback(chunk)
        return True

    def _dataReceived_FINISHED(self) -> bool:
        """
        Once C{finishCallback} has been invoked receipt of additional data
        raises L{RuntimeError} because it represents a programming error in
        the caller.
        """
        raise RuntimeError(
            "_ChunkedTransferDecoder.dataReceived called after last "
            "chunk was processed"
        )

    def dataReceived(self, data: bytes) -> None:
        """
        Interpret data from a request or response body which uses the
        I{chunked} Transfer-Encoding.
        """
        self._buffer += data
        goOn = True
        while goOn and self._buffer:
            goOn = getattr(self, "_dataReceived_" + self.state)()

    def noMoreData(self) -> None:
        """
        Verify that all data has been received.  If it has not been, raise
        L{_DataLoss}.
        """
        if self.state != "FINISHED":
            raise _DataLoss(
                "Chunked decoder in %r state, still expecting more data to "
                "get to 'FINISHED' state." % (self.state,)
            )

@implementer(interfaces.IPushProducer)
class _NoPushProducer:
    """
    A no-op version of L{interfaces.IPushProducer}, used to abstract over the
    possibility that a L{HTTPChannel} transport does not provide
    L{IPushProducer}.
    """

    def pauseProducing(self):
        """
        Pause producing data.

        Tells a producer that it has produced too much data to process for
        the time being, and to stop until resumeProducing() is called.
        """

    def resumeProducing(self):
        """
        Resume producing data.

        This tells a producer to re-add itself to the main loop and produce
        more data for its consumer.
        """

    def registerProducer(self, producer, streaming):
        """
        Register to receive data from a producer.

        @param producer: The producer to register.
        @param streaming: Whether this is a streaming producer or not.
        """

    def unregisterProducer(self):
        """
        Stop consuming data from a producer, without disconnecting.
        """

    def stopProducing(self):
        """
        IProducer.stopProducing
        """

@implementer(interfaces.ITransport, interfaces.IPushProducer, interfaces.IConsumer)
class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin):
    """
    A receiver for HTTP requests.

    The L{HTTPChannel} provides L{interfaces.ITransport} and
    L{interfaces.IConsumer} to the L{Request} objects it creates.  It also
    implements L{interfaces.IPushProducer} to C{self.transport}, allowing the
    transport to pause it.

    @ivar MAX_LENGTH: Maximum length for initial request line and each line
        from the header.

    @ivar _transferDecoder: L{None} or a decoder instance if the request body
        uses the I{chunked} Transfer-Encoding.
    @type _transferDecoder: L{_ChunkedTransferDecoder}

    @ivar maxHeaders: Maximum number of headers allowed per request.
    @type maxHeaders: C{int}

    @ivar totalHeadersSize: Maximum bytes for request line plus all headers
        from the request.
    @type totalHeadersSize: C{int}

    @ivar _receivedHeaderSize: Bytes received so far for the header.
    @type _receivedHeaderSize: C{int}

    @ivar _handlingRequest: Whether a request is currently being processed.
    @type _handlingRequest: L{bool}

    @ivar _dataBuffer: Any data that has been received from the connection
        while processing an outstanding request.
    @type _dataBuffer: L{list} of L{bytes}

    @ivar _networkProducer: Either the transport, if it provides
        L{interfaces.IPushProducer}, or a null implementation of
        L{interfaces.IPushProducer}.  Used to attempt to prevent the transport
        from producing excess data when we're responding to a request.
    @type _networkProducer: L{interfaces.IPushProducer}

    @ivar _requestProducer: If the L{Request} object or anything it calls
        registers itself as an L{interfaces.IProducer}, it will be stored here.
        This is used to create a producing pipeline: pause/resume producing
        methods will be propagated from the C{transport}, through the
        L{HTTPChannel} instance, to the c{_requestProducer}.

        The reason we proxy through the producing methods rather than the old
        behaviour (where we literally just set the L{Request} object as the
        producer on the transport) is because we want to be able to exert
        backpressure on the client to prevent it from sending in arbitrarily
        many requests without ever reading responses.  Essentially, if the
        client never reads our responses we will eventually stop reading its
        requests.

    @type _requestProducer: L{interfaces.IPushProducer}

    @ivar _requestProducerStreaming: A boolean that tracks whether the producer
        on the L{Request} side of this channel has registered itself as a
        L{interfaces.IPushProducer} or an L{interfaces.IPullProducer}.
    @type _requestProducerStreaming: L{bool} or L{None}

    @ivar _waitingForTransport: A boolean that tracks whether the transport has
        asked us to stop producing.  This is used to keep track of what we're
        waiting for: if the transport has asked us to stop producing then we
        don't want to unpause the transport until it asks us to produce again.
    @type _waitingForTransport: L{bool}

    @ivar abortTimeout: The number of seconds to wait after we attempt to shut
        the transport down cleanly to give up and forcibly terminate it.  This
        is only used when we time a connection out, to prevent errors causing
        the FD to get leaked.  If this is L{None}, we will wait forever.
    @type abortTimeout: L{int}

    @ivar _abortingCall: The L{twisted.internet.base.DelayedCall} that will be
        used to forcibly close the transport if it doesn't close cleanly.
    @type _abortingCall: L{twisted.internet.base.DelayedCall}

    @ivar _optimisticEagerReadSize: When a resource takes a long time to answer
        a request (via L{twisted.web.server.NOT_DONE_YET}, hopefully one day by
        a L{Deferred}), we would like to be able to let that resource know
        about the underlying transport disappearing as promptly as possible,
        via L{Request.notifyFinish}, and therefore via
        C{self.requests[...].connectionLost()} on this L{HTTPChannel}.

        However, in order to simplify application logic, we implement
        head-of-line blocking, and do not relay pipelined requests to the
        application until the previous request has been answered.  This means
        that said application cannot dispose of any entity-body that comes in
        from those subsequent requests, which may be arbitrarily large, and it
        may need to be buffered in memory.

        To implement this tradeoff between prompt notification when possible
        (in the most frequent case of non-pipelined requests) and correct
        behavior when not (say, if a client sends a very long-running GET
        request followed by a PUT request with a very large body) we will
        continue reading pipelined requests into C{self._dataBuffer} up to a
        given limit.

        C{_optimisticEagerReadSize} is the number of bytes we will accept from
        the client and buffer before pausing the transport.

        This behavior has been in place since Twisted 17.9.0 .

    @type _optimisticEagerReadSize: L{int}
    """

    maxHeaders = 500
    totalHeadersSize = 16384
    abortTimeout = 15

    length = 0
    persistent = 1
    __header = b""
    __first_line = 1
    __content = None

    # set in instances or subclasses
    requestFactory = Request

    _savedTimeOut = None
    _receivedHeaderCount = 0
    _receivedHeaderSize = 0
    _requestProducer = None
    _requestProducerStreaming = None
    _waitingForTransport = False
    _abortingCall = None
    _optimisticEagerReadSize = 0x4000
    _log = Logger()

    def __init__(self):
        # the request queue
        self.requests = []
        self._handlingRequest = False
        self._dataBuffer = []
        self._transferDecoder = None

    def connectionMade(self):
        self.setTimeout(self.timeOut)
        self._networkProducer = interfaces.IPushProducer(
            self.transport, _NoPushProducer()
        )
        self._networkProducer.registerProducer(self, True)

    def lineReceived(self, line):
        """
        Called for each line from request until the end of headers when
        it enters binary mode.
        """
        self.resetTimeout()

        self._receivedHeaderSize += len(line)
        if self._receivedHeaderSize > self.totalHeadersSize:
            self._respondToBadRequestAndDisconnect()
            return

        if self.__first_line:
            # if this connection is not persistent, drop any data which
            # the client (illegally) sent after the last request.
            if not self.persistent:
                self.dataReceived = self.lineReceived = lambda *args: None
                return

            # IE sends an extraneous empty line (\r\n) after a POST request;
            # eat up such a line, but only ONCE
            if not line and self.__first_line == 1:
                self.__first_line = 2
                return

            # create a new Request object
            if INonQueuedRequestFactory.providedBy(self.requestFactory):
                request = self.requestFactory(self)
            else:
                request = self.requestFactory(self, len(self.requests))
            self.requests.append(request)

            self.__first_line = 0

            parts = line.split()
            if len(parts) != 3:
                self._respondToBadRequestAndDisconnect()
                return
            command, request, version = parts
            try:
                command.decode("ascii")
            except UnicodeDecodeError:
                self._respondToBadRequestAndDisconnect()
                return

            self._command = command
            self._path = request
            self._version = version
        elif line == b"":
            # End of headers.
            if self.__header:
                ok = self.headerReceived(self.__header)
                # If the last header we got is invalid, we MUST NOT proceed
                # with processing. We'll have sent a 400 anyway, so just stop.
                if not ok:
                    return
            self.__header = b""
            self.allHeadersReceived()
            if self.length == 0:
                self.allContentReceived()
            else:
                self.setRawMode()
        elif line[0] in b" \t":
            # Continuation of a multi line header.
            self.__header += b" " + line.lstrip(b" \t")
        # Regular header line.
        # Processing of header line is delayed to allow accumulating multi
        # line headers.
        else:
            if self.__header:
                self.headerReceived(self.__header)
            self.__header = line

    def _finishRequestBody(self, data):
        self.allContentReceived()
        self._dataBuffer.append(data)

    def _maybeChooseTransferDecoder(self, header, data):
        """
        If the provided header is C{content-length} or
        C{transfer-encoding}, choose the appropriate decoder if any.

        Returns L{True} if the request can proceed and L{False} if not.
        """

        def fail():
            self._respondToBadRequestAndDisconnect()
            self.length = None
            return False

        # Can this header determine the length?
        if header == b"content-length":
            if not data.isdigit():
                return fail()
            try:
                length = int(data)
            except ValueError:
                return fail()
            newTransferDecoder = _IdentityTransferDecoder(
                length, self.requests[-1].handleContentChunk, self._finishRequestBody
            )
        elif header == b"transfer-encoding":
            # XXX Rather poorly tested code block, apparently only exercised by
            # test_chunkedEncoding
            if data.lower() == b"chunked":
                length = None
                newTransferDecoder = _ChunkedTransferDecoder(
                    self.requests[-1].handleContentChunk, self._finishRequestBody
                )
            elif data.lower() == b"identity":
                return True
            else:
                return fail()
        else:
            # It's not a length related header, so exit
            return True

        if self._transferDecoder is not None:
            return fail()
```

[Assistant]
diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py
index 1c598380ac..3b784f5e3c 100644
--- a/src/twisted/web/http.py
+++ b/src/twisted/web/http.py
@@ -2000,16 +2000,21 @@ class _ChunkedTransferDecoder:
         @returns: C{False}, as there is either insufficient data to continue,
             or no data remains.
         """
-        if (
-            self._receivedTrailerHeadersSize + len(self._buffer)
-            > self._maxTrailerHeadersSize
-        ):
-            raise _MalformedChunkedDataError("Trailer headers data is too long.")
-
         eolIndex = self._buffer.find(b"\r\n", self._start)

         if eolIndex == -1:
             # Still no end of network line marker found.
+            #
+            # Check if we've run up against the trailer size limit: if the next
+            # read contains the terminating CRLF then we'll have this many bytes
+            # of trailers (including the CRLFs).
+            minTrailerSize = (
+                self._receivedTrailerHeadersSize
+                + len(self._buffer)
+                + (1 if self._buffer.endswith(b"\r") else 2)
+            )
+            if minTrailerSize > self._maxTrailerHeadersSize:
+                raise _MalformedChunkedDataError("Trailer headers data is too long.")
             # Continue processing more data.
             return False

@@ -2019,6 +2024,8 @@ class _ChunkedTransferDecoder:
             del self._buffer[0 : eolIndex + 2]
             self._start = 0
             self._receivedTrailerHeadersSize += eolIndex + 2
+            if self._receivedTrailerHeadersSize > self._maxTrailerHeadersSize:
+                raise _MalformedChunkedDataError("Trailer headers data is too long.")
             return True

         # eolIndex in this part of code is equal to 0
@@ -2342,8 +2349,8 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin):
             self.__header = line

     def _finishRequestBody(self, data):
-        self.allContentReceived()
         self._dataBuffer.append(data)
+        self.allContentReceived()

     def _maybeChooseTransferDecoder(self, header, data):
         """


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-41810: Reflected cross-site scripting (CWE-79) in Twisted's web redirect helper. Any twisted.web Resource that returns redirectTo(request.args[...], request) (a common redirect pattern) is exploitable.

# Vulnerable source (src/twisted/web/_template_util.py)
```python
        response code.  This is intended to be returned from a render method,
        eg::

            def render_GET(self, request):
                return redirectTo(b"http://example.com/", request)
    """
    if not isinstance(URL, bytes):
        raise TypeError("URL must be bytes")
    request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
    request.redirect(URL)
    # FIXME: The URL should be HTML-escaped.
    # https://twistedmatrix.com/trac/ticket/9839
    content = b"""
<html>
    <head>
        <meta http-equiv=\"refresh\" content=\"0;URL=%(url)s\">
    </head>
    <body bgcolor=\"#FFFFFF\" text=\"#000000\">
    <a href=\"%(url)s\">click here</a>
    </body>
</html>
""" % {
        b"url": URL
    }
    return content

class Redirect(resource.Resource):
    """
    Resource that redirects to a specific URL.

    @ivar url: Redirect target URL to put in the I{Location} response header.
    @type url: L{bytes}
    """

    isLeaf = True

    def __init__(self, url: bytes):
        super().__init__()
        self.url = url
```

[Assistant]
diff --git a/src/twisted/web/_template_util.py b/src/twisted/web/_template_util.py
--- a/src/twisted/web/_template_util.py
+++ b/src/twisted/web/_template_util.py
@@ -92,7 +92,7 @@ def redirectTo(URL: bytes, request: IRequest) -> bytes:
     </body>
 </html>
 """ % {
-        b"url": URL
+        b"url": escape(URL.decode("utf-8")).encode("utf-8")
     }
     return content


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-45780: GRUB2 through 2.12 computes allocation sizes in its TAR filesystem parser by adding a size field from the archive header directly to a constant without overflow checking. A crafted TAR archive with a large name or link size field can cause the addition to overflow, resulting in an undersized heap allocation followed by a heap buffer overflow when the name or link content is copied into that buffer.

# Vulnerable source (grub-core/fs/tar.c)
```c
/* cpio.c - cpio and tar filesystem.  */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/misc.h>
#include <grub/disk.h>
#include <grub/archelp.h>

#include <grub/file.h>
#include <grub/mm.h>
#include <grub/dl.h>
#include <grub/i18n.h>

GRUB_MOD_LICENSE ("GPLv3+");

/* tar support */
#define MAGIC	"ustar"
struct head
{
  char name[100];
  char mode[8];
  char uid[8];
  char gid[8];
  char size[12];
  char mtime[12];
  char chksum[8];
  char typeflag;
  char linkname[100];
  char magic[6];
  char version[2];
  char uname[32];
  char gname[32];
  char devmajor[8];
  char devminor[8];
  char prefix[155];
} GRUB_PACKED;

static inline unsigned long long
read_number (const char *str, grub_size_t size)
{
  unsigned long long ret = 0;
  while (size-- && *str >= '0' && *str <= '7')
    ret = (ret << 3) | (*str++ & 0xf);
  return ret;
}

struct grub_archelp_data
{
  grub_disk_t disk;
  grub_off_t hofs, next_hofs;
  grub_off_t dofs;
  grub_off_t size;
  char *linkname;
  grub_size_t linkname_alloc;
};

static grub_err_t
grub_cpio_find_file (struct grub_archelp_data *data, char **name,
		     grub_int32_t *mtime,
		     grub_uint32_t *mode)
{
  struct head hd;
  int reread = 0, have_longname = 0, have_longlink = 0;

  data->hofs = data->next_hofs;

  for (reread = 0; reread < 3; reread++)
    {
      if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd))
	return grub_errno;

      if (!hd.name[0] && !hd.prefix[0])
	{
	  *mode = GRUB_ARCHELP_ATTR_END;
	  return GRUB_ERR_NONE;
	}

      if (grub_memcmp (hd.magic, MAGIC, sizeof (MAGIC) - 1))
	return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive");

      if (hd.typeflag == 'L')
	{
	  grub_err_t err;
	  grub_size_t namesize = read_number (hd.size, sizeof (hd.size));
	  *name = grub_malloc (namesize + 1);
	  if (*name == NULL)
	    return grub_errno;
	  err = grub_disk_read (data->disk, 0,
				data->hofs + GRUB_DISK_SECTOR_SIZE, namesize,
				*name);
	  (*name)[namesize] = 0;
	  if (err)
	    return err;
	  data->hofs += GRUB_DISK_SECTOR_SIZE
	    + ((namesize + GRUB_DISK_SECTOR_SIZE - 1) &
	       ~(GRUB_DISK_SECTOR_SIZE - 1));
	  have_longname = 1;
	  continue;
	}

      if (hd.typeflag == 'K')
	{
	  grub_err_t err;
	  grub_size_t linksize = read_number (hd.size, sizeof (hd.size));
	  if (data->linkname_alloc < linksize + 1)
	    {
	      char *n;
	      n = grub_calloc (2, linksize + 1);
	      if (!n)
		return grub_errno;
	      grub_free (data->linkname);
	      data->linkname = n;
	      data->linkname_alloc = 2 * (linksize + 1);
	    }

	  err = grub_disk_read (data->disk, 0,
				data->hofs + GRUB_DISK_SECTOR_SIZE, linksize,
				data->linkname);
	  if (err)
	    return err;
	  data->linkname[linksize] = 0;
	  data->hofs += GRUB_DISK_SECTOR_SIZE
	    + ((linksize + GRUB_DISK_SECTOR_SIZE - 1) &
	       ~(GRUB_DISK_SECTOR_SIZE - 1));
	  have_longlink = 1;
	  continue;
	}

      if (!have_longname)
	{
	  grub_size_t extra_size = 0;

	  while (extra_size < sizeof (hd.prefix)
		 && hd.prefix[extra_size])
	    extra_size++;
	  *name = grub_malloc (sizeof (hd.name) + extra_size + 2);
	  if (*name == NULL)
	    return grub_errno;
	  if (hd.prefix[0])
	    {
	      grub_memcpy (*name, hd.prefix, extra_size);
	      (*name)[extra_size++] = '/';
	    }
	  grub_memcpy (*name + extra_size, hd.name, sizeof (hd.name));
	  (*name)[extra_size + sizeof (hd.name)] = 0;
	}

      data->size = read_number (hd.size, sizeof (hd.size));
      data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE;
      data->next_hofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) &
			   ~(GRUB_DISK_SECTOR_SIZE - 1));
      if (mtime)
	*mtime = read_number (hd.mtime, sizeof (hd.mtime));
      if (mode)
	{
	  *mode = read_number (hd.mode, sizeof (hd.mode));
	  switch (hd.typeflag)
	    {
	      /* Hardlink.  */
	    case '1':
	      /* Symlink.  */
	    case '2':
	      *mode |= GRUB_ARCHELP_ATTR_LNK;
	      break;
	    case '0':
	      *mode |= GRUB_ARCHELP_ATTR_FILE;
	      break;
	    case '5':
	      *mode |= GRUB_ARCHELP_ATTR_DIR;
	      break;
	    }
	}
      if (!have_longlink)
	{
	  if (data->linkname_alloc < 101)
	    {
	      char *n;
	      n = grub_malloc (101);
	      if (!n)
		return grub_errno;
```

[Assistant]
diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c
index 646bce5eb..386c09022 100644
--- a/grub-core/fs/tar.c
+++ b/grub-core/fs/tar.c
@@ -25,6 +25,7 @@
 #include <grub/mm.h>
 #include <grub/dl.h>
 #include <grub/i18n.h>
+#include <grub/safemath.h>

 GRUB_MOD_LICENSE ("GPLv3+");

@@ -76,6 +77,7 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name,
 {
   struct head hd;
   int reread = 0, have_longname = 0, have_longlink = 0;
+  grub_size_t sz;

   data->hofs = data->next_hofs;
   *name = NULL;
@@ -98,7 +100,11 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name,
 	{
 	  grub_err_t err;
 	  grub_size_t namesize = read_number (hd.size, sizeof (hd.size));
-	  *name = grub_malloc (namesize + 1);
+
+	  if (grub_add (namesize, 1, &sz))
+	    return grub_error (GRUB_ERR_BAD_FS, N_("name size overflow"));
+
+	  *name = grub_malloc (sz);
 	  if (*name == NULL)
 	    return grub_errno;
 	  err = grub_disk_read (data->disk, 0,
@@ -118,15 +124,19 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name,
 	{
 	  grub_err_t err;
 	  grub_size_t linksize = read_number (hd.size, sizeof (hd.size));
-	  if (data->linkname_alloc < linksize + 1)
+
+	  if (grub_add (linksize, 1, &sz))
+	    return grub_error (GRUB_ERR_BAD_FS, N_("link size overflow"));
+
+	  if (data->linkname_alloc < sz)
 	    {
 	      char *n;
-	      n = grub_calloc (2, linksize + 1);
+	      n = grub_calloc (2, sz);
 	      if (!n)
 		return grub_errno;
 	      grub_free (data->linkname);
 	      data->linkname = n;
-	      data->linkname_alloc = 2 * (linksize + 1);
+	      data->linkname_alloc = 2 * (sz);
 	    }

 	  err = grub_disk_read (data->disk, 0,
@@ -149,7 +159,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name,
 	  while (extra_size < sizeof (hd.prefix)
 		 && hd.prefix[extra_size])
 	    extra_size++;
-	  *name = grub_malloc (sizeof (hd.name) + extra_size + 2);
+
+	  if (grub_add (sizeof (hd.name) + 2, extra_size, &sz))
+	    return grub_error (GRUB_ERR_BAD_FS, N_("long name size overflow"));
+	  *name = grub_malloc (sz);
 	  if (*name == NULL)
 	    return grub_errno;
 	  if (hd.prefix[0])


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-45781: GRUB2 copies symbolic link strings from UFS filesystem images into a heap-allocated buffer without verifying that the string fits in the buffer. When a filesystem image contains a link whose path is longer than the buffer, the copy writes past the buffer end into adjacent heap memory, potentially enabling arbitrary code execution during boot.

# Vulnerable source (grub-core/fs/ufs.c)
```c
  grpblk += grub_ufs_to_cpu32 (sblock->cylg_offset)
    * (group & (~grub_ufs_to_cpu32 (sblock->cylg_mask)));
#endif

  if (!inode)
    {
      inode = (char *) &data->inode;
      data->ino = ino;
    }

  grub_disk_read (data->disk,
		  ((grub_ufs_to_cpu32 (sblock->inoblk_offs) + grpblk)
		   << grub_ufs_to_cpu32 (data->sblock.log2_blksz))
		  + grpino / UFS_INODE_PER_BLOCK,
		  (grpino % UFS_INODE_PER_BLOCK)
		  * sizeof (struct grub_ufs_inode),
		  sizeof (struct grub_ufs_inode),
		  inode);

  return grub_errno;
}

/* Lookup the symlink the current inode points to.  INO is the inode
   number of the directory the symlink is relative to.  */
static grub_err_t
grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino)
{
  char *symlink;
  grub_size_t sz = INODE_SIZE (data);

  if (++data->linknest > GRUB_UFS_MAX_SYMLNK_CNT)
    return grub_error (GRUB_ERR_SYMLINK_LOOP, N_("too deep nesting of symlinks"));

  symlink = grub_malloc (sz + 1);
  if (!symlink)
    return grub_errno;
  /* Normally we should just check that data->inode.nblocks == 0.
     However old Linux doesn't maintain nblocks correctly and so it's always
     0. If size is bigger than inline space then the symlink is surely not
     inline.  */
  /* Check against zero is paylindromic, no need to swap.  */
  if (data->inode.nblocks == 0
      && INODE_SIZE (data) <= sizeof (data->inode.symlink))
    grub_strcpy (symlink, (char *) data->inode.symlink);
  else
    {
      if (grub_ufs_read_file (data, 0, 0, 0, sz, symlink) < 0)
	{
	  grub_free(symlink);
	  return grub_errno;
	}
    }
  symlink[sz] = '\0';

  /* The symlink is an absolute path, go back to the root inode.  */
  if (symlink[0] == '/')
    ino = GRUB_UFS_INODE;

  /* Now load in the old inode.  */
  if (grub_ufs_read_inode (data, ino, 0))
    {
      grub_free (symlink);
      return grub_errno;
    }

  grub_ufs_find_file (data, symlink);

  grub_free (symlink);

  return grub_errno;
}

/* Find the file with the pathname PATH on the filesystem described by
   DATA.  */
static grub_err_t
grub_ufs_find_file (struct grub_ufs_data *data, const char *path)
{
  const char *name;
  const char *next = path;
  unsigned int pos = 0;
  int dirino;
  char *filename;

  /* We reject filenames longer than the one we're looking
     for without reading, so this allocation is enough.  */
```

[Assistant]
diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c
index a354c92d9..01235101b 100644
--- a/grub-core/fs/ufs.c
+++ b/grub-core/fs/ufs.c
@@ -463,7 +463,7 @@ grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino)
   /* Check against zero is paylindromic, no need to swap.  */
   if (data->inode.nblocks == 0
       && INODE_SIZE (data) <= sizeof (data->inode.symlink))
-    grub_strcpy (symlink, (char *) data->inode.symlink);
+    grub_strlcpy (symlink, (char *) data->inode.symlink, sz);
   else
     {
       if (grub_ufs_read_file (data, 0, 0, 0, sz, symlink) < 0)


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-45782: GRUB2 through 2.12 reads HFS volume names from disk images and copies them into a bounded stack buffer without verifying that the name fits. An attacker with a crafted HFS disk image can provide a volume name that overflows the buffer.

# Vulnerable source (grub-core/fs/hfs.c)
```c
      grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem");
      goto fail;
    }

  /* Check if this is an embedded HFS+ filesystem.  */
  if (grub_be_to_cpu16 (data->sblock.embed_sig) == GRUB_HFS_EMBED_HFSPLUS_SIG)
    {
      grub_error (GRUB_ERR_BAD_FS, "embedded HFS+ filesystem");
      goto fail;
    }

  data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
  data->disk = disk;

  /* Lookup the root node of the extent overflow tree.  */
  first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block)
		  * GRUB_HFS_BLKS)
		 + grub_be_to_cpu16 (data->sblock.first_block));

  if (grub_disk_read (data->disk, first_block, 0,
		      sizeof (treehead), &treehead))
    goto fail;
  data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
  data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);

  /* Lookup the root node of the catalog tree.  */
  first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block)
		  * GRUB_HFS_BLKS)
		 + grub_be_to_cpu16 (data->sblock.first_block));
  if (grub_disk_read (data->disk, first_block, 0,
		      sizeof (treehead), &treehead))
    goto fail;
  data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
  data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);

  if (data->cat_size == 0
      || data->blksz < data->cat_size
      || data->blksz < data->ext_size)
    goto fail;

  /* Lookup the root directory node in the catalog tree using the
     volume name.  */
  key.parent_dir = grub_cpu_to_be32_compile_time (1);
  key.strlen = data->sblock.volname[0];
  grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));

  if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
			  0, (char *) &dir, sizeof (dir)) == 0)
    {
      grub_error (GRUB_ERR_BAD_FS, "cannot find the HFS root directory");
      goto fail;
    }

  if (grub_errno)
    goto fail;

  data->rootdir = grub_be_to_cpu32 (dir.dirid);

  return data;
 fail:
  grub_free (data);

  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
    grub_error (GRUB_ERR_BAD_FS, "not a HFS filesystem");

  return 0;
}

/* Compare the K1 and K2 catalog file keys using HFS character ordering.  */
static int
grub_hfs_cmp_catkeys (const struct grub_hfs_catalog_key *k1,
		      const struct grub_hfs_catalog_key *k2)
{
  /* Taken from hfsutils 3.2.6 and converted to a readable form */
  static const unsigned char hfs_charorder[256] = {
    [0x00] = 0,
    [0x01] = 1,
    [0x02] = 2,
    [0x03] = 3,
    [0x04] = 4,
    [0x05] = 5,
    [0x06] = 6,
    [0x07] = 7,
    [0x08] = 8,
    [0x09] = 9,
    [0x0A] = 10,
    [0x0B] = 11,
```

[Assistant]
diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c
index 91dc0e69c..920112b03 100644
--- a/grub-core/fs/hfs.c
+++ b/grub-core/fs/hfs.c
@@ -379,7 +379,7 @@ grub_hfs_mount (grub_disk_t disk)
      volume name.  */
   key.parent_dir = grub_cpu_to_be32_compile_time (1);
   key.strlen = data->sblock.volname[0];
-  grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1));
+  grub_strlcpy ((char *) key.str, (char *) (data->sblock.volname + 1), sizeof (key.str));

   if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
 			  0, (char *) &dir, sizeof (dir)) == 0)


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2024-5535: OpenSSL's SSL_select_next_proto function failed to validate that its protocol list arguments were correctly formed before processing them. The function accepted a zero-length or malformed list and would access memory beyond the end of the provided buffer while iterating over protocol names. Code that called this function with an NPN (Next Protocol Negotiation) list that was not properly validated upstream could trigger an out-of-bounds read, potentially leading to application crashes or information disclosure.

# Vulnerable source (ssl/ssl_lib.c)
```c
                return sc->session->ext.hostname;
        } else {
            if (!SSL_CONNECTION_IS_TLS13(sc) && sc->hit
                && sc->session->ext.hostname != NULL)
                return sc->session->ext.hostname;
        }
    }

    return sc->ext.hostname;
}

int SSL_get_servername_type(const SSL *s)
{
    if (SSL_get_servername(s, TLSEXT_NAMETYPE_host_name) != NULL)
        return TLSEXT_NAMETYPE_host_name;
    return -1;
}

/*
 * SSL_select_next_proto implements the standard protocol selection. It is
 * expected that this function is called from the callback set by
 * SSL_CTX_set_next_proto_select_cb. The protocol data is assumed to be a
 * vector of 8-bit, length prefixed byte strings. The length byte itself is
 * not included in the length. A byte string of length 0 is invalid. No byte
 * string may be truncated. The current, but experimental algorithm for
 * selecting the protocol is: 1) If the server doesn't support NPN then this
 * is indicated to the callback. In this case, the client application has to
 * abort the connection or have a default application level protocol. 2) If
 * the server supports NPN, but advertises an empty list then the client
 * selects the first protocol in its list, but indicates via the API that this
 * fallback case was enacted. 3) Otherwise, the client finds the first
 * protocol in the server's list that it supports and selects this protocol.
 * This is because it's assumed that the server has better information about
 * which protocol a client should use. 4) If the client doesn't support any
 * of the server's advertised protocols, then this is treated the same as
 * case 2. It returns either OPENSSL_NPN_NEGOTIATED if a common protocol was
 * found, or OPENSSL_NPN_NO_OVERLAP if the fallback case was reached.
 */
int SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
                          const unsigned char *server,
                          unsigned int server_len,
                          const unsigned char *client, unsigned int client_len)
{
    unsigned int i, j;
    const unsigned char *result;
    int status = OPENSSL_NPN_UNSUPPORTED;

    /*
     * For each protocol in server preference order, see if we support it.
     */
    for (i = 0; i < server_len;) {
        for (j = 0; j < client_len;) {
            if (server[i] == client[j] &&
                memcmp(&server[i + 1], &client[j + 1], server[i]) == 0) {
                /* We found a match */
                result = &server[i];
                status = OPENSSL_NPN_NEGOTIATED;
                goto found;
            }
            j += client[j];
            j++;
        }
        i += server[i];
        i++;
    }

    /* There's no overlap between our protocols and the server's list. */
    result = client;
    status = OPENSSL_NPN_NO_OVERLAP;

 found:
    *out = (unsigned char *)result + 1;
    *outlen = result[0];
    return status;
}

#ifndef OPENSSL_NO_NEXTPROTONEG
/*
 * SSL_get0_next_proto_negotiated sets *data and *len to point to the
 * client's requested protocol for this connection and returns 0. If the
 * client didn't request any protocol, then *data is set to NULL. Note that
 * the client can request any protocol it chooses. The value returned from
 * this function need not be a member of the list of supported protocols
 * provided by the callback.
 */
void SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
                                    unsigned *len)
{
    const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);

    if (sc == NULL) {
        /* We have no other way to indicate error */
        *data = NULL;
        *len = 0;
        return;
    }

    *data = sc->ext.npn;
    if (*data == NULL) {
        *len = 0;
    } else {
        *len = (unsigned int)sc->ext.npn_len;
    }
}

/*
 * SSL_CTX_set_npn_advertised_cb sets a callback that is called when
 * a TLS server needs a list of supported protocols for Next Protocol
 * Negotiation. The returned list must be in wire format.  The list is
 * returned by setting |out| to point to it and |outlen| to its length. This
 * memory will not be modified, but one should assume that the SSL* keeps a
 * reference to it. The callback should return SSL_TLSEXT_ERR_OK if it
 * wishes to advertise. Otherwise, no such extension will be included in the
 * ServerHello.
 */
void SSL_CTX_set_npn_advertised_cb(SSL_CTX *ctx,
                                   SSL_CTX_npn_advertised_cb_func cb,
```

[Assistant]
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 5ec6ac4b63dc5..4c20ac4bf1fe7 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3530,37 +3530,54 @@ int SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
                           unsigned int server_len,
                           const unsigned char *client, unsigned int client_len)
 {
-    unsigned int i, j;
-    const unsigned char *result;
-    int status = OPENSSL_NPN_UNSUPPORTED;
+    PACKET cpkt, csubpkt, spkt, ssubpkt;
+
+    if (!PACKET_buf_init(&cpkt, client, client_len)
+            || !PACKET_get_length_prefixed_1(&cpkt, &csubpkt)
+            || PACKET_remaining(&csubpkt) == 0) {
+        *out = NULL;
+        *outlen = 0;
+        return OPENSSL_NPN_NO_OVERLAP;
+    }
+
+    /*
+     * Set the default opportunistic protocol. Will be overwritten if we find
+     * a match.
+     */
+    *out = (unsigned char *)PACKET_data(&csubpkt);
+    *outlen = (unsigned char)PACKET_remaining(&csubpkt);

     /*
      * For each protocol in server preference order, see if we support it.
      */
-    for (i = 0; i < server_len;) {
-        for (j = 0; j < client_len;) {
-            if (server[i] == client[j] &&
-                memcmp(&server[i + 1], &client[j + 1], server[i]) == 0) {
-                /* We found a match */
-                result = &server[i];
-                status = OPENSSL_NPN_NEGOTIATED;
-                goto found;
+    if (PACKET_buf_init(&spkt, server, server_len)) {
+        while (PACKET_get_length_prefixed_1(&spkt, &ssubpkt)) {
+            if (PACKET_remaining(&ssubpkt) == 0)
+                continue; /* Invalid - ignore it */
+            if (PACKET_buf_init(&cpkt, client, client_len)) {
+                while (PACKET_get_length_prefixed_1(&cpkt, &csubpkt)) {
+                    if (PACKET_equal(&csubpkt, PACKET_data(&ssubpkt),
+                                     PACKET_remaining(&ssubpkt))) {
+                        /* We found a match */
+                        *out = (unsigned char *)PACKET_data(&ssubpkt);
+                        *outlen = (unsigned char)PACKET_remaining(&ssubpkt);
+                        return OPENSSL_NPN_NEGOTIATED;
+                    }
+                }
+                /* Ignore spurious trailing bytes in the client list */
+            } else {
+                /* This should never happen */
+                return OPENSSL_NPN_NO_OVERLAP;
             }
-            j += client[j];
-            j++;
         }
-        i += server[i];
-        i++;
+        /* Ignore spurious trailing bytes in the server list */
     }

-    /* There's no overlap between our protocols and the server's list. */
-    result = client;
-    status = OPENSSL_NPN_NO_OVERLAP;
-
- found:
-    *out = (unsigned char *)result + 1;
-    *outlen = result[0];
-    return status;
+    /*
+     * There's no overlap between our protocols and the server's list. We use
+     * the default opportunistic protocol selected earlier
+     */
+    return OPENSSL_NPN_NO_OVERLAP;
 }

 #ifndef OPENSSL_NO_NEXTPROTONEG