Skip to content

Commit 4619a48

Browse files
vrabaudjzern
authored andcommitted
Fix OOB write in BuildHuffmanTable.
First, BuildHuffmanTable is called to check if the data is valid. If it is and the table is not big enough, more memory is allocated. This will make sure that valid (but unoptimized because of unbalanced codes) streams are still decodable. Bug: chromium:1479274 Change-Id: I31c36dbf3aa78d35ecf38706b50464fd3d375741 (cherry picked from commit 902bc91) (cherry picked from commit 2af2626)
1 parent 6a319d4 commit 4619a48

File tree

4 files changed

+129
-43
lines changed

4 files changed

+129
-43
lines changed

src/dec/vp8l_dec.c

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,11 @@ static int ReadHuffmanCodeLengths(
253253
int symbol;
254254
int max_symbol;
255255
int prev_code_len = DEFAULT_CODE_LENGTH;
256-
HuffmanCode table[1 << LENGTHS_TABLE_BITS];
256+
HuffmanTables tables;
257257

258-
if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
259-
code_length_code_lengths,
260-
NUM_CODE_LENGTH_CODES)) {
258+
if (!VP8LHuffmanTablesAllocate(1 << LENGTHS_TABLE_BITS, &tables) ||
259+
!VP8LBuildHuffmanTable(&tables, LENGTHS_TABLE_BITS,
260+
code_length_code_lengths, NUM_CODE_LENGTH_CODES)) {
261261
goto End;
262262
}
263263

@@ -277,7 +277,7 @@ static int ReadHuffmanCodeLengths(
277277
int code_len;
278278
if (max_symbol-- == 0) break;
279279
VP8LFillBitWindow(br);
280-
p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
280+
p = &tables.curr_segment->start[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
281281
VP8LSetBitPos(br, br->bit_pos_ + p->bits);
282282
code_len = p->value;
283283
if (code_len < kCodeLengthLiterals) {
@@ -300,14 +300,16 @@ static int ReadHuffmanCodeLengths(
300300
ok = 1;
301301

302302
End:
303+
VP8LHuffmanTablesDeallocate(&tables);
303304
if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
304305
return ok;
305306
}
306307

307308
// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman
308309
// tree.
309310
static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
310-
int* const code_lengths, HuffmanCode* const table) {
311+
int* const code_lengths,
312+
HuffmanTables* const table) {
311313
int ok = 0;
312314
int size = 0;
313315
VP8LBitReader* const br = &dec->br_;
@@ -362,8 +364,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
362364
VP8LMetadata* const hdr = &dec->hdr_;
363365
uint32_t* huffman_image = NULL;
364366
HTreeGroup* htree_groups = NULL;
365-
HuffmanCode* huffman_tables = NULL;
366-
HuffmanCode* huffman_table = NULL;
367+
HuffmanTables* huffman_tables = &hdr->huffman_tables_;
367368
int num_htree_groups = 1;
368369
int num_htree_groups_max = 1;
369370
int max_alphabet_size = 0;
@@ -372,6 +373,10 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
372373
int* mapping = NULL;
373374
int ok = 0;
374375

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

435440
code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
436441
sizeof(*code_lengths));
437-
huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size,
438-
sizeof(*huffman_tables));
439442
htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
440443

441-
if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) {
444+
if (htree_groups == NULL || code_lengths == NULL ||
445+
!VP8LHuffmanTablesAllocate(num_htree_groups * table_size,
446+
huffman_tables)) {
442447
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
443448
goto Error;
444449
}
445450

446-
huffman_table = huffman_tables;
447451
for (i = 0; i < num_htree_groups_max; ++i) {
448452
// If the index "i" is unused in the Huffman image, just make sure the
449453
// coefficients are valid but do not store them.
@@ -468,19 +472,20 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
468472
int max_bits = 0;
469473
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
470474
int alphabet_size = kAlphabetSize[j];
471-
htrees[j] = huffman_table;
472475
if (j == 0 && color_cache_bits > 0) {
473476
alphabet_size += (1 << color_cache_bits);
474477
}
475-
size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table);
478+
size =
479+
ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_tables);
480+
htrees[j] = huffman_tables->curr_segment->curr_table;
476481
if (size == 0) {
477482
goto Error;
478483
}
479484
if (is_trivial_literal && kLiteralMap[j] == 1) {
480-
is_trivial_literal = (huffman_table->bits == 0);
485+
is_trivial_literal = (htrees[j]->bits == 0);
481486
}
482-
total_size += huffman_table->bits;
483-
huffman_table += size;
487+
total_size += htrees[j]->bits;
488+
huffman_tables->curr_segment->curr_table += size;
484489
if (j <= ALPHA) {
485490
int local_max_bits = code_lengths[0];
486491
int k;
@@ -515,14 +520,13 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
515520
hdr->huffman_image_ = huffman_image;
516521
hdr->num_htree_groups_ = num_htree_groups;
517522
hdr->htree_groups_ = htree_groups;
518-
hdr->huffman_tables_ = huffman_tables;
519523

520524
Error:
521525
WebPSafeFree(code_lengths);
522526
WebPSafeFree(mapping);
523527
if (!ok) {
524528
WebPSafeFree(huffman_image);
525-
WebPSafeFree(huffman_tables);
529+
VP8LHuffmanTablesDeallocate(huffman_tables);
526530
VP8LHtreeGroupsFree(htree_groups);
527531
}
528532
return ok;
@@ -1358,7 +1362,7 @@ static void ClearMetadata(VP8LMetadata* const hdr) {
13581362
assert(hdr != NULL);
13591363

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

16741678
if (dec == NULL) return 0;
16751679

1676-
assert(dec->hdr_.huffman_tables_ != NULL);
1680+
assert(dec->hdr_.huffman_tables_.root.start != NULL);
16771681
assert(dec->hdr_.htree_groups_ != NULL);
16781682
assert(dec->hdr_.num_htree_groups_ > 0);
16791683

src/dec/vp8li_dec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ typedef struct {
5151
uint32_t* huffman_image_;
5252
int num_htree_groups_;
5353
HTreeGroup* htree_groups_;
54-
HuffmanCode* huffman_tables_;
54+
HuffmanTables huffman_tables_;
5555
} VP8LMetadata;
5656

5757
typedef struct VP8LDecoder VP8LDecoder;

src/utils/huffman_utils.c

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -177,21 +177,24 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
177177
if (num_open < 0) {
178178
return 0;
179179
}
180-
if (root_table == NULL) continue;
181180
for (; count[len] > 0; --count[len]) {
182181
HuffmanCode code;
183182
if ((key & mask) != low) {
184-
table += table_size;
183+
if (root_table != NULL) table += table_size;
185184
table_bits = NextTableBitSize(count, len, root_bits);
186185
table_size = 1 << table_bits;
187186
total_size += table_size;
188187
low = key & mask;
189-
root_table[low].bits = (uint8_t)(table_bits + root_bits);
190-
root_table[low].value = (uint16_t)((table - root_table) - low);
188+
if (root_table != NULL) {
189+
root_table[low].bits = (uint8_t)(table_bits + root_bits);
190+
root_table[low].value = (uint16_t)((table - root_table) - low);
191+
}
192+
}
193+
if (root_table != NULL) {
194+
code.bits = (uint8_t)(len - root_bits);
195+
code.value = (uint16_t)sorted[symbol++];
196+
ReplicateValue(&table[key >> root_bits], step, table_size, code);
191197
}
192-
code.bits = (uint8_t)(len - root_bits);
193-
code.value = (uint16_t)sorted[symbol++];
194-
ReplicateValue(&table[key >> root_bits], step, table_size, code);
195198
key = GetNextKey(key, len);
196199
}
197200
}
@@ -211,25 +214,83 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
211214
((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES)
212215
// Cut-off value for switching between heap and stack allocation.
213216
#define SORTED_SIZE_CUTOFF 512
214-
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
217+
int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
215218
const int code_lengths[], int code_lengths_size) {
216-
int total_size;
219+
const int total_size =
220+
BuildHuffmanTable(NULL, root_bits, code_lengths, code_lengths_size, NULL);
217221
assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
218-
if (root_table == NULL) {
219-
total_size = BuildHuffmanTable(NULL, root_bits,
220-
code_lengths, code_lengths_size, NULL);
221-
} else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
222+
if (total_size == 0 || root_table == NULL) return total_size;
223+
224+
if (root_table->curr_segment->curr_table + total_size >=
225+
root_table->curr_segment->start + root_table->curr_segment->size) {
226+
// If 'root_table' does not have enough memory, allocate a new segment.
227+
// The available part of root_table->curr_segment is left unused because we
228+
// need a contiguous buffer.
229+
const int segment_size = root_table->curr_segment->size;
230+
struct HuffmanTablesSegment* next =
231+
(HuffmanTablesSegment*)WebPSafeMalloc(1, sizeof(*next));
232+
if (next == NULL) return 0;
233+
// Fill the new segment.
234+
// We need at least 'total_size' but if that value is small, it is better to
235+
// allocate a big chunk to prevent more allocations later. 'segment_size' is
236+
// therefore chosen (any other arbitrary value could be chosen).
237+
next->size = total_size > segment_size ? total_size : segment_size;
238+
next->start =
239+
(HuffmanCode*)WebPSafeMalloc(next->size, sizeof(*next->start));
240+
if (next->start == NULL) {
241+
WebPSafeFree(next);
242+
return 0;
243+
}
244+
next->curr_table = next->start;
245+
next->next = NULL;
246+
// Point to the new segment.
247+
root_table->curr_segment->next = next;
248+
root_table->curr_segment = next;
249+
}
250+
if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
222251
// use local stack-allocated array.
223252
uint16_t sorted[SORTED_SIZE_CUTOFF];
224-
total_size = BuildHuffmanTable(root_table, root_bits,
225-
code_lengths, code_lengths_size, sorted);
226-
} else { // rare case. Use heap allocation.
253+
BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
254+
code_lengths, code_lengths_size, sorted);
255+
} else { // rare case. Use heap allocation.
227256
uint16_t* const sorted =
228257
(uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
229258
if (sorted == NULL) return 0;
230-
total_size = BuildHuffmanTable(root_table, root_bits,
231-
code_lengths, code_lengths_size, sorted);
259+
BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
260+
code_lengths, code_lengths_size, sorted);
232261
WebPSafeFree(sorted);
233262
}
234263
return total_size;
235264
}
265+
266+
int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables) {
267+
// Have 'segment' point to the first segment for now, 'root'.
268+
HuffmanTablesSegment* const root = &huffman_tables->root;
269+
huffman_tables->curr_segment = root;
270+
// Allocate root.
271+
root->start = (HuffmanCode*)WebPSafeMalloc(size, sizeof(*root->start));
272+
if (root->start == NULL) return 0;
273+
root->curr_table = root->start;
274+
root->next = NULL;
275+
root->size = size;
276+
return 1;
277+
}
278+
279+
void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables) {
280+
HuffmanTablesSegment *current, *next;
281+
if (huffman_tables == NULL) return;
282+
// Free the root node.
283+
current = &huffman_tables->root;
284+
next = current->next;
285+
WebPSafeFree(current->start);
286+
current->start = NULL;
287+
current->next = NULL;
288+
current = next;
289+
// Free the following nodes.
290+
while (current != NULL) {
291+
next = current->next;
292+
WebPSafeFree(current->start);
293+
WebPSafeFree(current);
294+
current = next;
295+
}
296+
}

src/utils/huffman_utils.h

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,29 @@ typedef struct {
4343
// or non-literal symbol otherwise
4444
} HuffmanCode32;
4545

46+
// Contiguous memory segment of HuffmanCodes.
47+
typedef struct HuffmanTablesSegment {
48+
HuffmanCode* start;
49+
// Pointer to where we are writing into the segment. Starts at 'start' and
50+
// cannot go beyond 'start' + 'size'.
51+
HuffmanCode* curr_table;
52+
// Pointer to the next segment in the chain.
53+
struct HuffmanTablesSegment* next;
54+
int size;
55+
} HuffmanTablesSegment;
56+
57+
// Chained memory segments of HuffmanCodes.
58+
typedef struct HuffmanTables {
59+
HuffmanTablesSegment root;
60+
// Currently processed segment. At first, this is 'root'.
61+
HuffmanTablesSegment* curr_segment;
62+
} HuffmanTables;
63+
64+
// Allocates a HuffmanTables with 'size' contiguous HuffmanCodes. Returns 0 on
65+
// memory allocation error, 1 otherwise.
66+
int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables);
67+
void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables);
68+
4669
#define HUFFMAN_PACKED_BITS 6
4770
#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)
4871

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

86107
#ifdef __cplusplus

0 commit comments

Comments
 (0)