Headline
CVE-2023-44709: CWE-131 (Incorrect Calculation of Buffer Size) in plutosvg_load_from_memory · Issue #7 · sammycage/plutosvg
PlutoSVG commit 336c02997277a1888e6ccbbbe674551a0582e5c4 and before was discovered to contain an integer overflow via the component plutosvg_load_from_memory.
Summary
An integer overflow in the allocated size of calloc causes a segment fault.
It might lead to heap overflow and arbitrary code execution.
Steps to reproduce
code:
#include <plutosvg.h> #include <stdlib.h> #include <stdio.h>
int main(int argc, char* argv[]) { plutovg_surface_t* surface = plutosvg_load_from_file(argv[1], NULL, 0, 0, 96.0); return 0; }
run
$ ./example ./poc Segmentation fault
ASAN report
==16700==WARNING: AddressSanitizer failed to allocate 0xfffffffffef21b74 bytes
==16700==AddressSanitizer's allocator is terminating the process instead of returning 0
==16700==If you don't like this behavior set allocator_may_return_null=1
==16700==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/sanitizer_common/sanitizer_allocator.cc:218 "((0)) != (0)" (0x0, 0x0)
#0 0x7ffff6f01bf2 (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe9bf2)
#1 0x7ffff6f20575 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x108575)
#2 0x7ffff6f07332 (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xef332)
#3 0x7ffff6e3fe46 (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x27e46)
#4 0x7ffff6ef6b0a in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb0a)
#5 0x555555588ba1 in plutovg_surface_create (/home/waterfire/fuzz/example/plutosvg/build/example/example+0x34ba1)
#6 0x55555557a6aa in plutosvg_load_from_memory (/home/waterfire/fuzz/example/plutosvg/build/example/example+0x266aa)
#7 0x55555557aa21 in plutosvg_load_from_file (/home/waterfire/fuzz/example/plutosvg/build/example/example+0x26a21)
#8 0x55555556405a in main (/home/waterfire/fuzz/example/plutosvg/build/example/example+0x1005a)
#9 0x7ffff66aac86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
#10 0x555555563f19 in _start (/home/waterfire/fuzz/example/plutosvg/build/example/example+0xff19)
Analysis
plutosvg_load_from_memory does not check the size of width and height and calls plutovg_surface_create(width, height);.
In plutovg_surface_create:
plutovg_surface_t* plutovg_surface_create(int width, int height) { plutovg_surface_t* surface = malloc(sizeof(plutovg_surface_t)); surface->ref = 1; surface->owndata = 1; surface->data = calloc(1, (size_t)(width * height * 4)); surface->width = width; surface->height = height; surface->stride = width * 4; return surface; }
An integer overflow might happen when calculating width * height * 4. It might be better to check the sizes of width and height before the allocation.
PoC
poc.zip
I’ll make sure to dive into this matter as soon as my schedule permits. Thank you for bringing it to my attention!
A simple way to fix the bug is adding a size check like this
if((size_t)(width * height * 4) >= 0x80000000){
// error message
exit(0);
}
By the way, the allocation in function stbi_write_png_to_mem seems to have the same problem:
filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }
They can be fixed by the same way.
Steps to reproduce
code:
#include <plutosvg.h> #include <stdlib.h> #include <stdio.h>
int main(int argc, char* argv[]) { plutovg_surface_t* surface = plutosvg_load_from_file(argv[1], NULL, 0, 0, 96.0); if(surface == NULL) { printf(“Load failed\n”); return -1; } plutovg_surface_write_to_png(surface, “test.png”); plutovg_surface_destroy(surface); return 0; }
ASAN report
=================================================================
==32443==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000191 at pc 0x555555582f20 bp 0x7fffffffd740 sp 0x7fffffffd730
READ of size 1 at 0x602000000191 thread T0
#0 0x555555582f1f in stbiw__encode_png_line (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x2ef1f)
#1 0x555555583ff8 in stbi_write_png_to_mem (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x2fff8)
#2 0x5555555852cd in stbi_write_png (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x312cd)
#3 0x55555558950a in plutovg_surface_write_to_png (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x3550a)
#4 0x55555556408b in main (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x1008b)
#5 0x7ffff66aac86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
#6 0x555555563f19 in _start (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0xff19)
0x602000000191 is located 0 bytes to the right of 1-byte region [0x602000000190,0x602000000191)
allocated by thread T0 here:
#0 0x7ffff6ef6b40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
#1 0x555555589232 in plutovg_surface_write_to_png (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x35232)
#2 0x55555556408b in main (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x1008b)
#3 0x7ffff66aac86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/waterfire/fuzz/example/plutosvg/build/example/example_asan+0x2ef1f) in stbiw__encode_png_line
Shadow bytes around the buggy address:
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff8000: fa fa fd fd fa fa fd fd fa fa 01 fa fa fa fd fd
0x0c047fff8010: fa fa fd fd fa fa fd fa fa fa fd fa fa fa fd fa
0x0c047fff8020: fa fa fd fd fa fa fd fa fa fa fd fa fa fa fd fa
=>0x0c047fff8030: fa fa[01]fa fa fa 00 01 fa fa 01 fa fa fa fa fa
0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==32443==ABORTING
The poc does not cause segment fault, but can trigger heap overflow.
The allocation size of line_buffer is overflowed to 0, and the heap overflow is triggered in stbiw__encode_png_line when writing to line_buffer.
poc_heap_overflow.zip