VYPR
High severityCISA KEVNVD Advisory· Published Sep 12, 2023· Updated Oct 21, 2025

CVE-2023-4863

CVE-2023-4863

Description

Heap buffer overflow in libwebp in Google Chrome prior to 116.0.5845.187 and libwebp 1.3.2 allowed a remote attacker to perform an out of bounds memory write via a crafted HTML page. (Chromium security severity: Critical)

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
libwebp-sys2crates.io
< 0.1.80.1.8
libwebp-syscrates.io
< 0.9.30.9.3
electronnpm
>= 22.0.0, < 22.3.2422.3.24
electronnpm
>= 24.0.0, < 24.8.324.8.3
electronnpm
>= 25.0.0, < 25.8.125.8.1
electronnpm
>= 26.0.0, < 26.2.126.2.1
electronnpm
>= 27.0.0-beta.1, < 27.0.0-beta.227.0.0-beta.2
SkiaSharpNuGet
>= 2.0.0, < 2.88.62.88.6
github.com/chai2010/webpGo
>= 1.1.2, < 1.4.01.4.0
PillowPyPI
< 10.0.110.0.1
webpcrates.io
< 0.2.60.2.6
magick.net-q16-anycpuNuGet
< 13.3.013.3.0
magick.net-q16-hdri-anycpuNuGet
< 13.3.013.3.0
magick.net-q16-x64NuGet
< 13.3.013.3.0
magick.net-q8-anycpuNuGet
< 13.3.013.3.0
magick.net-q8-openmp-x64NuGet
< 13.3.013.3.0
magick.net-q8-x64NuGet
< 13.3.013.3.0
github.com/chai2010/webpGo
< 0.0.0-20250406010349-76805d5a88600.0.0-20250406010349-76805d5a8860
github.com/chai2010/webpGo
>= 0.0.0, < 1.1.2-0.20250406010349-76805d5a88601.1.2-0.20250406010349-76805d5a8860

Affected products

2
  • Range: 116.0.5845.187
  • Google/libwebpv5
    Range: 1.3.2

Patches

3
9d4c56e63abe

chore: Bump libwebp-sys and crate version

https://github.com/jaredforth/webppeaseeSep 14, 2023via ghsa
1 file changed · +2 2
  • Cargo.toml+2 2 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "webp"
    -version = "0.2.5"
    +version = "0.2.6"
     authors = ["Jared Forth <jaredforthdev@gmail.com>"]
     edition = "2018"
     
    @@ -15,7 +15,7 @@ keywords = ["image", "webp", "conversion"]
     categories = ["external-ffi-bindings"]
     
     [dependencies]
    -libwebp-sys = "0.9.1"
    +libwebp-sys = "0.9.3"
     image = { version = "^0.24.0", default-features = false, optional = true }
     
     [features]
    
4560c473a76e

Patch CVE-2023-4863

1 file changed · +1 1
  • c_src+1 1 modified
    @@ -1 +1 @@
    -Subproject commit b557776962a3dcc985d83bd4ed94e1e2e50d0fa2
    +Subproject commit a450afed44162f898ba5014e21efbad539fe3f0e
    
902bc9190331

Fix OOB write in BuildHuffmanTable.

https://github.com/webmproject/libwebpVincent RabaudSep 7, 2023via ghsa
4 files changed · +129 43
  • src/dec/vp8l_dec.c+25 21 modified
    @@ -262,11 +262,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;
       }
     
    @@ -286,7 +286,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) {
    @@ -309,14 +309,16 @@ static int ReadHuffmanCodeLengths(
       ok = 1;
     
      End:
    +  VP8LHuffmanTablesDeallocate(&tables);
       if (!ok) return VP8LSetError(dec, 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* const code_lengths,
    +                           HuffmanTables* const table) {
       int ok = 0;
       int size = 0;
       VP8LBitReader* const br = &dec->br_;
    @@ -367,8 +369,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;
       const int max_alphabet_size =
    @@ -378,6 +379,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;
    @@ -429,16 +434,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)) {
         VP8LSetError(dec, 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.
    @@ -463,19 +467,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;
    @@ -510,14 +515,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;
    @@ -1352,7 +1356,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_);
    @@ -1666,7 +1670,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);
     
    
  • src/dec/vp8li_dec.h+1 1 modified
    @@ -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;
    
  • src/utils/huffman_utils.c+79 18 modified
    @@ -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;
    +  }
    +}
    
  • src/utils/huffman_utils.h+24 3 modified
    @@ -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
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

77

News mentions

0

No linked articles in our index yet.