summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lodepng.cpp135
-rw-r--r--lodepng.h7
-rw-r--r--lodepng_unittest.cpp17
3 files changed, 107 insertions, 52 deletions
diff --git a/lodepng.cpp b/lodepng.cpp
index d628b14..6568bba 100644
--- a/lodepng.cpp
+++ b/lodepng.cpp
@@ -1,5 +1,5 @@
/*
-LodePNG version 20180326
+LodePNG version 20180611
Copyright (c) 2005-2018 Lode Vandevenne
@@ -39,7 +39,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
#endif /*_MSC_VER */
-const char* LODEPNG_VERSION_STRING = "20180326";
+const char* LODEPNG_VERSION_STRING = "20180611";
/*
This source file is built up in the following large parts. The code sections
@@ -62,11 +62,17 @@ from here.*/
#ifdef LODEPNG_COMPILE_ALLOCATORS
static void* lodepng_malloc(size_t size)
{
+#ifdef LODEPNG_MAX_ALLOC
+ if(size > LODEPNG_MAX_ALLOC) return 0;
+#endif
return malloc(size);
}
static void* lodepng_realloc(void* ptr, size_t new_size)
{
+#ifdef LODEPNG_MAX_ALLOC
+ if(new_size > LODEPNG_MAX_ALLOC) return 0;
+#endif
return realloc(ptr, new_size);
}
@@ -86,6 +92,8 @@ void lodepng_free(void* ptr);
/* ////////////////////////////////////////////////////////////////////////// */
/* ////////////////////////////////////////////////////////////////////////// */
+#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
+
/*
Often in case of an error a value is assigned to a variable and then it breaks
out of a loop (to go to the cleanup phase of a function). This macro does that.
@@ -1455,11 +1463,11 @@ static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned
{
hash->val[wpos] = (int)hashval;
if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval];
- hash->head[hashval] = (unsigned)wpos;
+ hash->head[hashval] = (int)wpos;
hash->zeros[wpos] = numzeros;
if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros];
- hash->headz[numzeros] = (unsigned)wpos;
+ hash->headz[numzeros] = (int)wpos;
}
/*
@@ -2700,32 +2708,75 @@ unsigned lodepng_can_have_alpha(const LodePNGColorMode* info)
|| lodepng_has_palette_alpha(info);
}
-size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color)
+size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth)
{
- /*will not overflow for any color type if roughly w * h < 268435455*/
- size_t bpp = lodepng_get_bpp(color);
- size_t n = w * h;
+ size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
+ size_t n = (size_t)w * (size_t)h;
return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8;
}
-size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth)
+size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color)
{
- /*will not overflow for any color type if roughly w * h < 268435455*/
- size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
- size_t n = w * h;
- return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8;
+ return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth);
}
#ifdef LODEPNG_COMPILE_PNG
#ifdef LODEPNG_COMPILE_DECODER
-/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/
+
+/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer,
+and in addition has one extra byte per line: the filter byte. So this gives a larger
+result than lodepng_get_raw_size. */
static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color)
{
- /*will not overflow for any color type if roughly w * h < 268435455*/
size_t bpp = lodepng_get_bpp(color);
- size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8;
- return h * line;
+ /* + 1 for the filter byte, and possibly plus padding bits per line */
+ size_t line = ((size_t)(w / 8) * bpp) + 1 + ((w & 7) * bpp + 7) / 8;
+ return (size_t)h * line;
+}
+
+/* Safely check if multiplying two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_mulofl(size_t a, size_t b, size_t* result)
+{
+ *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */
+ return (a != 0 && *result / a != b);
+}
+
+/* Safely check if adding two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_addofl(size_t a, size_t b, size_t* result)
+{
+ *result = a + b; /* Unsigned addition is well defined and safe in C90 */
+ return *result < a;
+}
+
+/*Safely checks whether size_t overflow can be caused due to amount of pixels.
+This check is overcautious rather than precise. If this check indicates no overflow,
+you can safely compute in a size_t (but not an unsigned):
+-(size_t)w * (size_t)h * 8
+-amount of bytes in IDAT (including filter, padding and Adam7 bytes)
+-amount of bytes in raw color model
+Returns 1 if overflow possible, 0 if not.
+*/
+static int lodepng_pixel_overflow(unsigned w, unsigned h,
+ const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor)
+{
+ size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor));
+ size_t numpixels, total;
+ size_t line; /* bytes per line in worst case */
+
+ if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1;
+ if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */
+
+ /* Bytes per scanline with the expression "(w / 8) * bpp) + ((w & 7) * bpp + 7) / 8" */
+ if(lodepng_mulofl((size_t)(w / 8), bpp, &line)) return 1;
+ if(lodepng_addofl(line, ((w & 7) * bpp + 7) / 8, &line)) return 1;
+
+ if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */
+ if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */
+
+ return 0; /* no overflow */
}
#endif /*LODEPNG_COMPILE_DECODER*/
#endif /*LODEPNG_COMPILE_PNG*/
@@ -3457,7 +3508,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
{
size_t i;
ColorTree tree;
- size_t numpixels = w * h;
+ size_t numpixels = (size_t)w * (size_t)h;
unsigned error = 0;
if(lodepng_color_mode_equal(mode_out, mode_in))
@@ -3577,7 +3628,7 @@ unsigned lodepng_get_color_profile(LodePNGColorProfile* profile,
unsigned error = 0;
size_t i;
ColorTree tree;
- size_t numpixels = w * h;
+ size_t numpixels = (size_t)w * (size_t)h;
unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0;
unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1;
@@ -3772,14 +3823,16 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
{
LodePNGColorProfile prof;
unsigned error = 0;
- unsigned i, n, palettebits, palette_ok;
+ unsigned palettebits, palette_ok;
+ size_t i, n;
+ size_t numpixels = (size_t)w * (size_t)h;
lodepng_color_profile_init(&prof);
error = lodepng_get_color_profile(&prof, image, w, h, mode_in);
if(error) return error;
mode_out->key_defined = 0;
- if(prof.key && w * h <= 16)
+ if(prof.key && numpixels <= 16)
{
prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/
prof.key = 0;
@@ -3788,7 +3841,7 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
n = prof.numcolors;
palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8));
palette_ok = n <= 256 && prof.bits <= 8;
- if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/
+ if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/
if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/
if(palette_ok)
@@ -4535,7 +4588,6 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
ucvector idat; /*the data from idat chunks*/
ucvector scanlines;
size_t predict;
- size_t numpixels;
size_t outsize = 0;
/*for unknown chunk order*/
@@ -4550,13 +4602,10 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/
if(state->error) return;
- numpixels = *w * *h;
-
- /*multiplication overflow*/
- if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92);
- /*multiplication overflow possible further below. Allows up to 2^31-1 pixel
- bytes with 16-bit RGBA, the rest is room for filter bytes.*/
- if(numpixels > 268435455) CERROR_RETURN(state->error, 92);
+ if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw))
+ {
+ CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/
+ }
ucvector_init(&idat);
chunk = &in[33]; /*first byte of the first chunk after the header*/
@@ -4595,7 +4644,9 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
if(lodepng_chunk_type_equals(chunk, "IDAT"))
{
size_t oldsize = idat.size;
- if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/);
+ size_t newsize;
+ if(lodepng_addofl(oldsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95);
+ if(!ucvector_resize(&idat, newsize)) CERROR_BREAK(state->error, 83 /*alloc fail*/);
for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i];
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
critical_pos = 3;
@@ -4698,21 +4749,20 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
If the decompressed size does not match the prediction, the image must be corrupt.*/
if(state->info_png.interlace_method == 0)
{
- /*The extra *h is added because this are the filter bytes every scanline starts with*/
- predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h;
+ predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color);
}
else
{
/*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/
const LodePNGColorMode* color = &state->info_png.color;
predict = 0;
- predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3);
- if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3);
- predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3);
- if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2);
- predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2);
- if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1);
- predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1);
+ predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color);
+ if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color);
+ predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color);
+ if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color);
+ predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color);
+ if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color);
+ predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color);
}
if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/
if(!state->error)
@@ -5701,7 +5751,7 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize,
if(!lodepng_color_mode_equal(&state->info_raw, &info.color))
{
unsigned char* converted;
- size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8;
+ size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8;
converted = (unsigned char*)lodepng_malloc(size);
if(!converted && size) state->error = 83; /*alloc fail*/
@@ -6014,9 +6064,10 @@ const char* lodepng_error_text(unsigned code)
/*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/
case 90: return "windowsize must be a power of two";
case 91: return "invalid decompressed idat size";
- case 92: return "too many pixels, not supported";
+ case 92: return "integer overflow due to too many pixels";
case 93: return "zero width or height is invalid";
case 94: return "header chunk must have a size of 13 bytes";
+ case 95: return "integer overflow with combined idat chunk size";
}
return "unknown error code";
}
diff --git a/lodepng.h b/lodepng.h
index 8e0f742..6c56c1d 100644
--- a/lodepng.h
+++ b/lodepng.h
@@ -1,5 +1,5 @@
/*
-LodePNG version 20180326
+LodePNG version 20180611
Copyright (c) 2005-2018 Lode Vandevenne
@@ -410,7 +410,7 @@ typedef struct LodePNGInfo
/*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
unsigned compression_method;/*compression method of the original file. Always 0.*/
unsigned filter_method; /*filter method of the original file*/
- unsigned interlace_method; /*interlace method of the original file*/
+ unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
@@ -1614,6 +1614,7 @@ yyyymmdd.
Some changes aren't backwards compatible. Those are indicated with a (!)
symbol.
+*) 11 jun 2018: less restrictive check for pixel size integer overflow
*) 14 jan 2018: allow optionally ignoring a few more recoverable errors
*) 17 sep 2017: fix memory leak for some encoder input error cases
*) 27 nov 2016: grey+alpha auto color model detection bugfix
@@ -1765,5 +1766,5 @@ Domain: gmail dot com.
Account: lode dot vandevenne.
-Copyright (c) 2005-2017 Lode Vandevenne
+Copyright (c) 2005-2018 Lode Vandevenne
*/
diff --git a/lodepng_unittest.cpp b/lodepng_unittest.cpp
index 975f2b4..1395416 100644
--- a/lodepng_unittest.cpp
+++ b/lodepng_unittest.cpp
@@ -1,7 +1,7 @@
/*
LodePNG Unit Test
-Copyright (c) 2005-2016 Lode Vandevenne
+Copyright (c) 2005-2018 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -30,8 +30,9 @@ Testing instructions:
*) Ensure no tests commented out below or early return in doMain
-*) Compile with g++ with all warnings and run the unit test
+*) Compile with g++ or clang++ with all warnings and run the unit test
g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -O3 && ./a.out
+clang++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -O3 && ./a.out
*) Compile with pure ISO C90 and all warnings:
mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/example_decode.c -ansi -pedantic -Wall -Wextra -O3 ; mv lodepng.c lodepng.cpp
@@ -66,7 +67,7 @@ g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEP
g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEPNG_NO_COMPILE_ENCODER
rm *.o
-*) analyze met clang:
+*) analyze with clang:
clang++ lodepng.cpp --analyze
More verbose:
clang++ --analyze -Xanalyzer -analyzer-output=text lodepng.cpp
@@ -75,8 +76,11 @@ clang++ --analyze -Xanalyzer -analyzer-output=html lodepng.cpp
*) check for memory leaks and vulnerabilities with valgrind
comment out the large files tests because they're slow with valgrind
-g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -pedantic -ansi -O3
-valgrind --leak-check=full --track-origins=yes ./a.out
+g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -pedantic -ansi -O3 -DLODEPNG_MAX_ALLOC=100000000 && valgrind --leak-check=full --track-origins=yes ./a.out
+
+*) Try with clang++ and address sanitizer (to get line numbers, make sure 'llvm' is also installed to get 'llvm-symbolizer'
+clang++ -fsanitize=address lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -O3 && ASAN_OPTIONS=allocator_may_return_null=1 ./a.out
+clang++ -fsanitize=address lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -g3 && ASAN_OPTIONS=allocator_may_return_null=1 ./a.out
*) remove "#include <iostream>" from lodepng.cpp if it's still in there
cat lodepng.cpp | grep iostream
@@ -89,8 +93,7 @@ cat lodepng.cpp | grep "#include <"
*) check year in copyright message at top of all files as well as at bottom of lodepng.h
*) check examples/sdl.cpp with the png test suite images (the "x" ones are expected to show error)
-g++ -I ./ lodepng.cpp examples/example_sdl.cpp -Wall -Wextra -pedantic -ansi -O3 -lSDL -o showpng
-./showpng testdata/PngSuite/''*.png
+g++ -I ./ lodepng.cpp examples/example_sdl.cpp -Wall -Wextra -pedantic -ansi -O3 -lSDL -o showpng && ./showpng testdata/PngSuite/''*.png
*) strip trailing spaces and ensure consistent newlines