OpenPrinting / cups

OpenPrinting CUPS Sources
https://openprinting.github.io/cups
Apache License 2.0
1.01k stars 182 forks source link

Use-after-free push_stack in /src/cups/cups/raster-interpret.c #768

Closed Jminis closed 1 year ago

Jminis commented 1 year ago

Description

Hello, while performing fuzzing based on the information registered in OSS-Fuzz, a UAF crash was discovered. I would like to share some basic analysis and crash data related to this. (Additionally, this issue does not have a significantly detrimental effect on the program.)

Crash Log

/out/FuzzCUPS -rss_limit_mb=2560 -timeout=25 FuzzCUPS_poc/cups--FuzzCUPS--crash-ec73921dbc52d3ce45efccb47567ba46-2023-07-27-16:03:10 # /tmp/FuzzCUPS_corpus < /dev/null
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2666470492
INFO: Loaded 1 modules   (10782 inline 8-bit counters): 10782 [0x724ea0, 0x7278be),
INFO: Loaded 1 PC tables (10782 PCs): 10782 [0x7278c0,0x751aa0),
/out/FuzzCUPS: Running 1 inputs 1 time(s) each.
Running: FuzzCUPS_poc/cups--FuzzCUPS--crash-ec73921dbc52d3ce45efccb47567ba46-2023-07-27-16:03:10
=================================================================
==12==ERROR: AddressSanitizer: heap-use-after-free on address 0x61d0000007d0 at pc 0x000000530bd1 bp 0x7ffc688612a0 sp 0x7ffc68860a70
READ of size 72 at 0x61d0000007d0 thread T0
SCARINESS: 54 (multi-byte-read-heap-use-after-free)
    #0 0x530bd0 in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
    #1 0x5778d7 in memcpy /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34:10
    #2 0x5778d7 in push_stack /src/cups/cups/raster-interpret.c:955:3
    #3 0x5778d7 in copy_stack /src/cups/cups/raster-interpret.c:730:10
    #4 0x573196 in _cupsRasterExecPS /src/cups/cups/raster-interpret.c
    #5 0x56ee5c in LLVMFuzzerTestOneInput /src/cups/fuzzer/FuzzCUPS.c:45:5
    #6 0x4406b3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
    #7 0x42be12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
    #8 0x4316bc in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
    #9 0x45abf2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #10 0x7f677bc91082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)
    #11 0x421fdd in _start (/out/FuzzCUPS+0x421fdd)

DEDUP_TOKEN: __asan_memcpy--memcpy--push_stack
0x61d0000007d0 is located 1872 bytes inside of 2304-byte region [0x61d000000080,0x61d000000980)
freed by thread T0 here:
    #0 0x531c4c in __interceptor_realloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:85:3
    #1 0x577844 in push_stack /src/cups/cups/raster-interpret.c:944:17
    #2 0x577844 in copy_stack /src/cups/cups/raster-interpret.c:730:10
    #3 0x573196 in _cupsRasterExecPS /src/cups/cups/raster-interpret.c
    #4 0x56ee5c in LLVMFuzzerTestOneInput /src/cups/fuzzer/FuzzCUPS.c:45:5
    #5 0x4406b3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
    #6 0x42be12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
    #7 0x4316bc in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
    #8 0x45abf2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #9 0x7f677bc91082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)

DEDUP_TOKEN: __interceptor_realloc--push_stack--copy_stack
previously allocated by thread T0 here:
    #0 0x531a1e in __interceptor_calloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:77:3
    #1 0x570d1b in new_stack /src/cups/cups/raster-interpret.c:899:19
    #2 0x570d1b in _cupsRasterExecPS /src/cups/cups/raster-interpret.c:529:13
    #3 0x56ee5c in LLVMFuzzerTestOneInput /src/cups/fuzzer/FuzzCUPS.c:45:5
    #4 0x4406b3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
    #5 0x42be12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
    #6 0x4316bc in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
    #7 0x45abf2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #8 0x7f677bc91082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)

DEDUP_TOKEN: __interceptor_calloc--new_stack--_cupsRasterExecPS
SUMMARY: AddressSanitizer: heap-use-after-free /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3 in __asan_memcpy
Shadow bytes around the buggy address:
  0x0c3a7fff80a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a7fff80b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a7fff80c0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a7fff80d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a7fff80e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c3a7fff80f0: fd fd fd fd fd fd fd fd fd fd[fd]fd fd fd fd fd
  0x0c3a7fff8100: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a7fff8110: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a7fff8120: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c3a7fff8130: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3a7fff8140: 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
==12==ABORTING

Analyze

From the debugging results, the obj structure was in a freed state at memcpy(temp, obj, sizeof(_cups_ps_obj_t)); in the push_stack function below. The point at which it is freed seems to be when temp = realloc(st->objs, (size_t)st->alloc_objs * sizeof(_cups_ps_obj_t))) == NULL, as the memory of the heap area pointed to by obj is freed by realloc.

static _cups_ps_obj_t   *       /* O - New object */
push_stack(_cups_ps_stack_t *st,    /* I - Stack */
           _cups_ps_obj_t   *obj)   /* I - Object */
{
  _cups_ps_obj_t    *temp;      /* New object */

  if (st->num_objs >= st->alloc_objs)
  {

    st->alloc_objs += 32;

    if ((temp = realloc(st->objs, (size_t)st->alloc_objs *
                                  sizeof(_cups_ps_obj_t))) == NULL)
      return (NULL);

    st->objs = temp;
    memset(temp + st->num_objs, 0, 32 * sizeof(_cups_ps_obj_t));
  }

  temp = st->objs + st->num_objs;
  st->num_objs ++;

  memcpy(temp, obj, sizeof(_cups_ps_obj_t));

  return (temp);
}

Here, obj is an argument at the call of push_stack in copy_stack, and it was pointing to the st structure that is freed in realloc.

static int              /* O - 0 on success, -1 on error */
copy_stack(_cups_ps_stack_t *st,    /* I - Stack */
           int              c)      /* I - Number of objects to copy */
{
  int   n;              /* Index */

  if (c < 0)
    return (-1);
  else if (c == 0)
    return (0);

  if ((n = st->num_objs - c) < 0)
    return (-1);

  while (c > 0)
  {
    if (!push_stack(st, st->objs + n))
      return (-1);

    n ++;
    c --;
  }

  return (0);
}
michaelrsweet commented 1 year ago

Simple enough fix - just need to make a temporary copy of the stack object before copying...

[master d2edfc664] Fix a bug in the copy stack code (Issue #768)

[2.4.x cd01b4d1a] Fix a bug in the copy stack code (Issue #768)