Skip to content

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

@Jminis

Description

@Jminis

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions