mirror of
https://github.com/glennrp/libpng.git
synced 2026-10-22 20:29:09 +03:00
test: Add regression tests for CVE-2026-46675
This tests commit 6c7783b151.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -164,6 +164,7 @@ pngprefix.h
|
||||
/pngfix
|
||||
/pnggetset
|
||||
/pngimage
|
||||
/pngreadend
|
||||
/pngstest
|
||||
/pngtest
|
||||
/pngunknown
|
||||
|
||||
@@ -393,6 +393,9 @@ set(pngstest_sources
|
||||
set(pnggetset_sources
|
||||
contrib/libtests/pnggetset.c
|
||||
)
|
||||
set(pngreadend_sources
|
||||
contrib/libtests/pngreadend.c
|
||||
)
|
||||
set(pngunknown_sources
|
||||
contrib/libtests/pngunknown.c
|
||||
)
|
||||
@@ -564,6 +567,17 @@ if(PNG_TESTS AND PNG_SHARED)
|
||||
png_add_test(NAME pnggetset
|
||||
COMMAND pnggetset)
|
||||
|
||||
# pngreadend test:
|
||||
# Reading the end of a PNG without reading the image.
|
||||
add_executable(pngreadend ${pngreadend_sources})
|
||||
target_link_libraries(pngreadend
|
||||
PRIVATE png_shared)
|
||||
|
||||
png_add_test(NAME pngreadend
|
||||
COMMAND pngreadend
|
||||
FILES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/testpngs/crashers/stale_zTXt_input.png"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/contrib/testpngs/crashers/stale_iCCP_input.png")
|
||||
|
||||
# pngvalid tests:
|
||||
# Internal validation of standard and progressive reading,
|
||||
# transforms, and gamma handling.
|
||||
|
||||
@@ -13,7 +13,7 @@ ACLOCAL_AMFLAGS = -I scripts/autoconf
|
||||
|
||||
# test programs - run on make check, make distcheck
|
||||
if ENABLE_TESTS
|
||||
check_PROGRAMS= pngtest pnggetset pngunknown pngstest pngvalid pngimage pngcp
|
||||
check_PROGRAMS= pngtest pnggetset pngreadend pngunknown pngstest pngvalid pngimage pngcp
|
||||
if HAVE_CLOCK_GETTIME
|
||||
check_PROGRAMS += timepng
|
||||
endif
|
||||
@@ -45,6 +45,9 @@ pngtest_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la
|
||||
pnggetset_SOURCES = contrib/libtests/pnggetset.c
|
||||
pnggetset_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la
|
||||
|
||||
pngreadend_SOURCES = contrib/libtests/pngreadend.c
|
||||
pngreadend_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la
|
||||
|
||||
pngvalid_SOURCES = contrib/libtests/pngvalid.c
|
||||
pngvalid_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la
|
||||
|
||||
@@ -77,6 +80,7 @@ if ENABLE_TESTS
|
||||
TESTS =\
|
||||
tests/pngtest-all\
|
||||
tests/pnggetset\
|
||||
tests/pngreadend\
|
||||
tests/pngvalid-gamma-16-to-8\
|
||||
tests/pngvalid-gamma-alpha-mode\
|
||||
tests/pngvalid-gamma-background\
|
||||
@@ -260,6 +264,7 @@ pngtest.o: pnglibconf.h
|
||||
contrib/libtests/makepng.o: pnglibconf.h
|
||||
contrib/libtests/pnggetset.o: pnglibconf.h
|
||||
contrib/libtests/pngimage.o: pnglibconf.h
|
||||
contrib/libtests/pngreadend.o: pnglibconf.h
|
||||
contrib/libtests/pngstest.o: pnglibconf.h
|
||||
contrib/libtests/pngunknown.o: pnglibconf.h
|
||||
contrib/libtests/pngvalid.o: pnglibconf.h
|
||||
|
||||
116
contrib/libtests/pngreadend.c
Normal file
116
contrib/libtests/pngreadend.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/* pngreadend.c
|
||||
*
|
||||
* Copyright (c) 2026 Cosmin Truta
|
||||
*
|
||||
* This code is released under the libpng license.
|
||||
* For conditions of distribution and use, see the disclaimer
|
||||
* and license in png.h
|
||||
*
|
||||
* Call png_read_end right after png_read_info, without reading the image.
|
||||
* This call sequence ends in a libpng error on every input, even on a valid
|
||||
* PNG. The test passes on such an error, and fails only on a crash or on a
|
||||
* report from a memory sanitizer.
|
||||
*
|
||||
* Before the fix for CVE-2026-46675, png_read_end resumed inflation from input
|
||||
* left over in the read buffer of a zTXt chunk, which a subsequent pCAL chunk
|
||||
* had freed by requesting a larger one (stale_zTXt_input.png), or in an
|
||||
* automatic array local to png_handle_iCCP after that function had returned
|
||||
* (stale_iCCP_input.png). Both test files declare an invalid zlib window size
|
||||
* on purpose, so that the stale read happens in libpng's own window check,
|
||||
* which the sanitizer instruments, rather than inside zlib, which is usually
|
||||
* built without it.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef PNG_FREESTANDING_TESTS
|
||||
# include <png.h>
|
||||
#else
|
||||
# include "../../png.h"
|
||||
#endif
|
||||
|
||||
/* AddressSanitizer detects an access to an automatic array after its function
|
||||
* has returned only if it puts such arrays on a separate stack, which is
|
||||
* optional.
|
||||
*/
|
||||
const char *__asan_default_options(void);
|
||||
|
||||
const char *
|
||||
__asan_default_options(void)
|
||||
{
|
||||
return "detect_stack_use_after_return=1";
|
||||
}
|
||||
|
||||
#if defined(PNG_SEQUENTIAL_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
|
||||
static int
|
||||
read_info_then_end(const char *file_name)
|
||||
{
|
||||
FILE *fp;
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
|
||||
fp = fopen(file_name, "rb");
|
||||
if (fp == NULL)
|
||||
{
|
||||
fprintf(stderr, "pngreadend: %s: cannot open\n", file_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (png_ptr == NULL)
|
||||
{
|
||||
fprintf(stderr, "pngreadend: png_create_read_struct failed\n");
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL)
|
||||
{
|
||||
fprintf(stderr, "pngreadend: png_create_info_struct failed\n");
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr)) == 0)
|
||||
{
|
||||
png_init_io(png_ptr, fp);
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
png_read_end(png_ptr, NULL);
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
#if defined(PNG_SEQUENTIAL_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; ++i)
|
||||
{
|
||||
printf("Testing png_read_end after png_read_info on %s\n", argv[i]);
|
||||
fflush(stdout);
|
||||
if (read_info_then_end(argv[i]) != 0)
|
||||
result = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
fprintf(stderr, "pngreadend: no support for sequential read or stdio\n");
|
||||
return 77;
|
||||
#endif
|
||||
}
|
||||
BIN
contrib/testpngs/crashers/stale_iCCP_input.png
Normal file
BIN
contrib/testpngs/crashers/stale_iCCP_input.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 B |
BIN
contrib/testpngs/crashers/stale_zTXt_input.png
Normal file
BIN
contrib/testpngs/crashers/stale_zTXt_input.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 232 B |
7
tests/pngreadend
Executable file
7
tests/pngreadend
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# pngreadend test:
|
||||
# Reading the end of a PNG without reading the image.
|
||||
exec ./pngreadend \
|
||||
"${srcdir}/contrib/testpngs/crashers/stale_zTXt_input.png" \
|
||||
"${srcdir}/contrib/testpngs/crashers/stale_iCCP_input.png"
|
||||
Reference in New Issue
Block a user