1
0
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:
Cosmin Truta
2026-10-21 20:54:30 +03:00
parent 18ea3e9404
commit 682e5fe800
7 changed files with 144 additions and 1 deletions

1
.gitignore vendored
View File

@@ -164,6 +164,7 @@ pngprefix.h
/pngfix
/pnggetset
/pngimage
/pngreadend
/pngstest
/pngtest
/pngunknown

View File

@@ -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.

View File

@@ -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

View 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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

7
tests/pngreadend Executable file
View 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"