libpng
libpng copied to clipboard
An infinite loop in png_read_png->..->png_write_row
Summary
A infinite loop bug found in png_read_png
.
Remote attackers could leverage this vulnerability to cause a denial-of-service via a crafted PNG file.
POC
#include <png.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <vector>
#include <fstream>
#include <iostream>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Initialize libpng variables
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
// Create a FILE pointer to read the input data
FILE *in_file = fmemopen((void *)data, size, "rb");
// Set up the read callback function
png_set_read_fn(png_ptr, (png_voidp)in_file, [](png_structp png_ptr, png_bytep data, png_size_t length) {
fread(data, 1, length, (FILE *)png_get_io_ptr(png_ptr));
});
// Read the PNG image
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
return 0;
}
POC input
timeout-f74021412fba530904cddd63e3033f1527d52d76
Version
Found on version of 2023/06/07. Reproducible on the master branch.
Compile commands
# export the flags.
SANITIZER_FLAGS="-O2 -fsanitize=address,undefined -fsanitize-address-use-after-scope -g "
FUZZER_FLAGS="-fsanitize=fuzzer-no-link -fno-omit-frame-pointer -g -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION $SANITIZER_FLAGS"
export CFLAGS="${CFLAGS:-} $FUZZER_FLAGS"
export CXXFLAGS="${CXXFLAGS:-} $FUZZER_FLAGS"
# build the libpng library.
cd $SRC/libpng
autoreconf -f -i
./configure
make -j$(nproc) clean
make -j$(nproc) libpng16.la
Compile the poc program
clang++ -fsanitize=fuzzer -O0 -g -fsanitize=address,undefined -ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -I/src/libpng/include poc.cc -o test.out /src/libpng/lib/libpng16.so
Reproduce Step
./test.out timeout-f74021412fba530904cddd63e3033f1527d52d76
Additional Information
When the variable i = 0xff
(image_height = 0x100) in the loop from lines 751-755, the png_read_row(png_ptr, *rp, NULL);
will hang.
//pngread.c
void PNGAPI
png_read_image(png_structrp png_ptr, png_bytepp image) {
...
for (j = 0; j < pass; j++)
{
rp = image;
751 for (i = 0; i < image_height; i++)
752 {
753 png_read_row(png_ptr, *rp, NULL);
754 rp++;
755 }
}
The program finally hang at the below loop.
png_read_IDAT_data(png_structrp png_ptr, png_bytep output,
png_alloc_size_t avail_out)
{
...
do
{
... // hang
}while (avail_out > 0);
Stack trace
...
[#3] 0x7ffff76278d4 → png_read_IDAT_data(png_ptr=0x61a000000080, output=0x0, avail_out=0x91bd0800)
[#4] 0x7ffff7628f13 → png_read_finish_IDAT(png_ptr=0x61a000000080)
[#5] 0x7ffff762c960 → png_read_finish_row(png_ptr=0x61a000000080)
[#6] 0x7ffff74cc279 → png_read_row(png_ptr=0x61a000000080, row=0x617000037d00 "\003\002\001\006\004\002\t\006\003\f\b\004\017\n\005\022\f\006\025\016\a\030\020\b\033\022\t\036\024\n!\026\v$\030\f'\032\r*\034\016-\036\0170 \0203\"\0216$\0229&\023<(\024?*\025B,\026E.\027H0\030K2\031N4\032Q6\033T8\034W:\035Z<\036]>\037`@ cB!fD\"iF#lH$oJ%rL&uN'xP({R)~T*\201V+\204X,\207Z-\212\\.\215^/\220`0\223b1\226d2\231f3\234h4\237j5\242l6\245n7\250p8\253r9\256t:\261v;\264x<\267z=\272|>\275~?\300\200@ÂAƄBɆC̈DϊEҌFՎGؐHےIޔJ\341\226K\344\230L\347\232M\352\234N\355\236O\360\240P\363\242Q\366\244R\371\246S\374\250T\377\252U\002\254V\005\256W\b\260X\v\262Y\016\264Z\021\266[\024\270\\\027\272]\032\274^\035\276_ \300`#\302a&\304b)\306c,\310d/\312e2\314f5\316g8\320h;\322i>\324jA\326kD\330lG\332mJ\334nM\336oP\340pS\342qV\344rY\346s\\\350t_\352ub\354ve\356wh\360xk\362yn\364zq\366{t\370|w\372}z\374~}\376\177\200", dsp_row=0x0)
[#7] 0x7ffff74d050f → png_read_image(png_ptr=0x61a000000080, image=0x61d000000080)
[#8] 0x7ffff74d805e → png_read_png(png_ptr=0x61a000000080, info_ptr=0x6130000003c0, transforms=0x0, params=0x0)
[#9] 0x5555556d8ef4 → LLVMFuzzerTestOneInput(data=0x613000000200 "\211PNG\r\n\032\n", size=0x160)
I suggest you submit a small program and the input file that will repro this. As reported the bug is incredible.
@jbowler Hi, I have provided the PoC program and PoC input at: timeout_poc.tar.gz
You can reproduce this issue by running:
poc.out timeout-f74021412fba530904cddd63e3033f1527d52d76
@jbowler Hi, I have provided the PoC program and PoC input at: [timeout_poc.tar.gz]
You need to remove that file, it is reported by Chrome as containing a virus. I've separately reported this to github.com
Please do not post compiled programs here. They are not useful in bug reports. What I'm asking for is a simple example which compiles, links, runs and demonstrates the problem. A program of this size is likely to be inappropriate even if you provide the source code.
But apart from that your code is wrong; your read function does no error handling so when it reaches the end of the file (which it does because the enormous IDAT at the end is truncated) it just keeps on reading.
@ctruta: application bug (bad read function)