Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CWE-131 (Incorrect Calculation of Buffer Size) in plutosvg_load_from_memory #7

Open
W4terf1re opened this issue Sep 21, 2023 · 3 comments
Labels
bug Something isn't working

Comments

@W4terf1re
Copy link

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

@sammycage sammycage added the bug Something isn't working label Sep 21, 2023
@sammycage
Copy link
Owner

I'll make sure to dive into this matter as soon as my schedule permits. Thank you for bringing it to my attention!

@W4terf1re
Copy link
Author

W4terf1re commented Sep 22, 2023

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);
}

@W4terf1re
Copy link
Author

W4terf1re commented Sep 22, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants