Skip to content

Commit

Permalink
handle integer overflow in lodepng_chunk_next and lodepng_chunk_find
Browse files Browse the repository at this point in the history
Fixes issue 123
  • Loading branch information
lvandeve committed Jan 12, 2020
1 parent 9652b36 commit b4539da
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 90 deletions.
18 changes: 7 additions & 11 deletions examples/example_png_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ Display the names and sizes of all chunks in the PNG file.
*/
void displayChunkNames(const std::vector<unsigned char>& buffer) {
// Listing chunks is based on the original file, not the decoded png info.
const unsigned char *chunk, *begin, *end, *next;
const unsigned char *chunk, *end;
end = &buffer.back() + 1;
begin = chunk = &buffer.front() + 8;
chunk = &buffer.front() + 8;

std::cout << std::endl << "Chunks:" << std::endl;
std::cout << " type: length(s)";
std::string last_type;
while(chunk + 8 < end && chunk >= begin) {
while(chunk < end && end - chunk >= 8) {
char type[5];
lodepng_chunk_type(type, chunk);
if(std::string(type).size() != 4) {
Expand All @@ -116,9 +116,7 @@ void displayChunkNames(const std::vector<unsigned char>& buffer) {

std::cout << lodepng_chunk_length(chunk) << ", ";

next = lodepng_chunk_next_const(chunk);
if (next <= chunk) break; // integer overflow
chunk = next;
chunk = lodepng_chunk_next_const(chunk, end);
}
std::cout << std::endl;
}
Expand Down Expand Up @@ -200,13 +198,13 @@ void displayFilterTypes(const std::vector<unsigned char>& buffer, bool ignore_ch
}

//Read literal data from all IDAT chunks
const unsigned char *chunk, *begin, *end, *next;
const unsigned char *chunk, *begin, *end;
end = &buffer.back() + 1;
begin = chunk = &buffer.front() + 8;

std::vector<unsigned char> zdata;

while(chunk + 8 < end && chunk >= begin) {
while(chunk < end && end - chunk >= 8) {
char type[5];
lodepng_chunk_type(type, chunk);
if(std::string(type).size() != 4) {
Expand All @@ -227,9 +225,7 @@ void displayFilterTypes(const std::vector<unsigned char>& buffer, bool ignore_ch
}
}

next = lodepng_chunk_next_const(chunk);
if (next <= chunk) break; // integer overflow
chunk = next;
chunk = lodepng_chunk_next_const(chunk, end);
}

//Decompress all IDAT data
Expand Down
69 changes: 41 additions & 28 deletions lodepng.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
LodePNG version 20191219
LodePNG version 20200112
Copyright (c) 2005-2019 Lode Vandevenne
Copyright (c) 2005-2020 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
Expand Down Expand Up @@ -44,7 +44,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 = "20191219";
const char* LODEPNG_VERSION_STRING = "20200112";

/*
This source file is built up in the following large parts. The code sections
Expand Down Expand Up @@ -135,6 +135,14 @@ static size_t lodepng_strlen(const char* a) {
#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x))

#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)
/* 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;
}
#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)*/

#ifdef LODEPNG_COMPILE_DECODER
/* Safely check if multiplying two integers will overflow (no undefined
Expand All @@ -144,13 +152,6 @@ static int lodepng_mulofl(size_t a, size_t b, size_t* result) {
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;
}

#ifdef LODEPNG_COMPILE_ZLIB
/* Safely check if a + b > c, even if overflow could happen. */
static int lodepng_gtofl(size_t a, size_t b, size_t c) {
Expand Down Expand Up @@ -2515,50 +2516,61 @@ void lodepng_chunk_generate_crc(unsigned char* chunk) {
lodepng_set32bitInt(chunk + 8 + length, CRC);
}

unsigned char* lodepng_chunk_next(unsigned char* chunk) {
unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end) {
if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
&& chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
/* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
return chunk + 8;
} else {
unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
return chunk + total_chunk_length;
size_t total_chunk_length;
unsigned char* result;
if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
result = chunk + total_chunk_length;
if(result < chunk) return end; /*pointer overflow*/
return result;
}
}

const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) {
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) {
if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
&& chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
/* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
return chunk + 8;
} else {
unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
return chunk + total_chunk_length;
size_t total_chunk_length;
const unsigned char* result;
if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
result = chunk + total_chunk_length;
if(result < chunk) return end; /*pointer overflow*/
return result;
}
}

unsigned char* lodepng_chunk_find(unsigned char* chunk, const unsigned char* end, const char type[5]) {
unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]) {
for(;;) {
if(chunk + 12 >= end) return 0;
if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
if(lodepng_chunk_type_equals(chunk, type)) return chunk;
chunk = lodepng_chunk_next(chunk);
chunk = lodepng_chunk_next(chunk, end);
}
}

const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) {
for(;;) {
if(chunk + 12 >= end) return 0;
if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
if(lodepng_chunk_type_equals(chunk, type)) return chunk;
chunk = lodepng_chunk_next_const(chunk);
chunk = lodepng_chunk_next_const(chunk, end);
}
}

unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) {
unsigned i;
unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
size_t total_chunk_length, new_length;
unsigned char *chunk_start, *new_buffer;
size_t new_length = (*outlength) + total_chunk_length;
if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/

if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77;
if(lodepng_addofl(*outlength, total_chunk_length, &new_length)) return 77;

new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
if(!new_buffer) return 83; /*alloc fail*/
Expand All @@ -2575,8 +2587,9 @@ unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned l
const char* type, const unsigned char* data) {
unsigned i;
unsigned char *chunk, *new_buffer;
size_t new_length = (*outlength) + length + 12;
if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/
size_t new_length = *outlength;
if(lodepng_addofl(new_length, length, &new_length)) return 77;
if(lodepng_addofl(new_length, 12, &new_length)) return 77;
new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
if(!new_buffer) return 83; /*alloc fail*/
(*out) = new_buffer;
Expand Down Expand Up @@ -4868,7 +4881,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
}

if(!IEND) chunk = lodepng_chunk_next_const(chunk);
if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
}

if (state->info_png.color.colortype == LCT_PALETTE
Expand Down Expand Up @@ -5750,7 +5763,7 @@ static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t data
while((size_t)(inchunk - data) < datasize) {
CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk));
out->allocsize = out->size; /*fix the allocsize again*/
inchunk = lodepng_chunk_next(inchunk);
inchunk = lodepng_chunk_next(inchunk, data + datasize);
}
return 0;
}
Expand Down
43 changes: 23 additions & 20 deletions lodepng.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
LodePNG version 20191219
LodePNG version 20200112
Copyright (c) 2005-2019 Lode Vandevenne
Copyright (c) 2005-2020 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
Expand Down Expand Up @@ -856,16 +856,16 @@ Input must be at the beginning of a chunk (result of a previous lodepng_chunk_ne
or the 8th byte of a PNG file which always has the first chunk), or alternatively may
point to the first byte of the PNG file (which is not a chunk but the magic header, the
function will then skip over it and return the first real chunk).
Expects at least 8 readable bytes of memory in the input pointer.
Will output pointer to the start of the next chunk or the end of the file if there
is no more chunk after this. Start this process at the 8th byte of the PNG file.
Will output pointer to the start of the next chunk, or at or beyond end of the file if there
is no more chunk after this or possibly if the chunk is corrupt.
Start this process at the 8th byte of the PNG file.
In a non-corrupt PNG file, the last chunk should have name "IEND".
*/
unsigned char* lodepng_chunk_next(unsigned char* chunk);
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk);
unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end);
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end);

/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/
unsigned char* lodepng_chunk_find(unsigned char* chunk, const unsigned char* end, const char type[5]);
unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]);
const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]);

/*
Expand Down Expand Up @@ -1775,14 +1775,17 @@ symbol.
Not all changes are listed here, the commit history in github lists more:
https://github.com/lvandeve/lodepng
*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
overflow checks.
*) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables.
*) 15 jun 2019 (!): auto_choose_color API changed (for bugfix: don't use palette
if gray ICC profile) and non-ICC LodePNGColorProfile renamed to LodePNGColorStats.
*) 15 jun 2019: (!) auto_choose_color API changed (for bugfix: don't use palette
if gray ICC profile) and non-ICC LodePNGColorProfile renamed to
LodePNGColorStats.
*) 30 dec 2018: code style changes only: removed newlines before opening braces.
*) 10 sep 2018: added way to inspect metadata chunks without full decoding.
*) 19 aug 2018 (!): fixed color mode bKGD is encoded with and made it use
*) 19 aug 2018: (!) fixed color mode bKGD is encoded with and made it use
palette index in case of palette.
*) 10 aug 2018 (!): added support for gAMA, cHRM, sRGB and iCCP chunks. This
*) 10 aug 2018: (!) added support for gAMA, cHRM, sRGB and iCCP chunks. This
change is backwards compatible unless you relied on unknown_chunks for those.
*) 11 jun 2018: less restrictive check for pixel size integer overflow
*) 14 jan 2018: allow optionally ignoring a few more recoverable errors
Expand All @@ -1802,25 +1805,25 @@ Not all changes are listed here, the commit history in github lists more:
*) 22 dec 2013: Power of two windowsize required for optimization.
*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key.
*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png).
*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_"
*) 11 mar 2013: (!) Bugfix with custom free. Changed from "my" to "lodepng_"
prefix for the custom allocators and made it possible with a new #define to
use custom ones in your project without needing to change lodepng's code.
*) 28 jan 2013: Bugfix with color key.
*) 27 okt 2012: Tweaks in text chunk keyword length error handling.
*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode.
*) 8 okt 2012: (!) Added new filter strategy (entropy) and new auto color mode.
(no palette). Better deflate tree encoding. New compression tweak settings.
Faster color conversions while decoding. Some internal cleanups.
*) 23 sep 2012: Reduced warnings in Visual Studio a little bit.
*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions
*) 1 sep 2012: (!) Removed #define's for giving custom (de)compression functions
and made it work with function pointers instead.
*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc
and free functions and toggle #defines from compiler flags. Small fixes.
*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible.
*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed
*) 6 may 2012: (!) Made plugging in custom zlib/deflate functions more flexible.
*) 22 apr 2012: (!) Made interface more consistent, renaming a lot. Removed
redundant C++ codec classes. Reduced amount of structs. Everything changed,
but it is cleaner now imho and functionality remains the same. Also fixed
several bugs and shrunk the implementation code. Made new samples.
*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best
*) 6 nov 2011: (!) By default, the encoder now automatically chooses the best
PNG color model and bit depth, based on the amount and type of colors of the
raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color.
*) 9 okt 2011: simpler hash chain implementation for the encoder.
Expand All @@ -1829,7 +1832,7 @@ Not all changes are listed here, the commit history in github lists more:
A bug with the PNG filtertype heuristic was fixed, so that it chooses much
better ones (it's quite significant). A setting to do an experimental, slow,
brute force search for PNG filter types is added.
*) 17 aug 2011 (!): changed some C zlib related function names.
*) 17 aug 2011: (!) changed some C zlib related function names.
*) 16 aug 2011: made the code less wide (max 120 characters per line).
*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors.
*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled.
Expand Down Expand Up @@ -1937,5 +1940,5 @@ Domain: gmail dot com.
Account: lode dot vandevenne.
Copyright (c) 2005-2019 Lode Vandevenne
Copyright (c) 2005-2020 Lode Vandevenne
*/
9 changes: 6 additions & 3 deletions lodepng_unittest.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
LodePNG Unit Test
Copyright (c) 2005-2019 Lode Vandevenne
Copyright (c) 2005-2020 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
Expand Down Expand Up @@ -1290,13 +1290,14 @@ void createComplexPNG(std::vector<unsigned char>& png) {

std::string extractChunkNames(const std::vector<unsigned char>& png) {
const unsigned char* chunk = &png[8];
const unsigned char* end = &png.back() + 1;
char name[5];
std::string result = "";
for(;;) {
lodepng_chunk_type(name, chunk);
result += (std::string(" ") + name);
if(std::string(name) == "IEND") break;
chunk = lodepng_chunk_next_const(chunk);
chunk = lodepng_chunk_next_const(chunk, end);
assertTrue(chunk < &png.back(), "jumped out of chunks");
}
return result;
Expand Down Expand Up @@ -3671,7 +3672,9 @@ int main() {
doMain();
}
catch(...) {
std::cout << "error!" << std::endl;
std::cout << std::endl;
std::cout << "caught error!" << std::endl;
std::cout << "*** TEST FAILED ***" << std::endl;
}

return 0;
Expand Down
Loading

0 comments on commit b4539da

Please sign in to comment.