Skip to content

Commit b4539da

Browse files
committed
handle integer overflow in lodepng_chunk_next and lodepng_chunk_find
Fixes issue 123
1 parent 9652b36 commit b4539da

File tree

7 files changed

+95
-90
lines changed

7 files changed

+95
-90
lines changed

examples/example_png_info.cpp

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,14 @@ Display the names and sizes of all chunks in the PNG file.
9393
*/
9494
void displayChunkNames(const std::vector<unsigned char>& buffer) {
9595
// Listing chunks is based on the original file, not the decoded png info.
96-
const unsigned char *chunk, *begin, *end, *next;
96+
const unsigned char *chunk, *end;
9797
end = &buffer.back() + 1;
98-
begin = chunk = &buffer.front() + 8;
98+
chunk = &buffer.front() + 8;
9999

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

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

119-
next = lodepng_chunk_next_const(chunk);
120-
if (next <= chunk) break; // integer overflow
121-
chunk = next;
119+
chunk = lodepng_chunk_next_const(chunk, end);
122120
}
123121
std::cout << std::endl;
124122
}
@@ -200,13 +198,13 @@ void displayFilterTypes(const std::vector<unsigned char>& buffer, bool ignore_ch
200198
}
201199

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

207205
std::vector<unsigned char> zdata;
208206

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

230-
next = lodepng_chunk_next_const(chunk);
231-
if (next <= chunk) break; // integer overflow
232-
chunk = next;
228+
chunk = lodepng_chunk_next_const(chunk, end);
233229
}
234230

235231
//Decompress all IDAT data

lodepng.cpp

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
LodePNG version 20191219
2+
LodePNG version 20200112
33
4-
Copyright (c) 2005-2019 Lode Vandevenne
4+
Copyright (c) 2005-2020 Lode Vandevenne
55
66
This software is provided 'as-is', without any express or implied
77
warranty. In no event will the authors be held liable for any damages
@@ -44,7 +44,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
4444
#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
4545
#endif /*_MSC_VER */
4646

47-
const char* LODEPNG_VERSION_STRING = "20191219";
47+
const char* LODEPNG_VERSION_STRING = "20200112";
4848

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

138+
#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)
139+
/* Safely check if adding two integers will overflow (no undefined
140+
behavior, compiler removing the code, etc...) and output result. */
141+
static int lodepng_addofl(size_t a, size_t b, size_t* result) {
142+
*result = a + b; /* Unsigned addition is well defined and safe in C90 */
143+
return *result < a;
144+
}
145+
#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)*/
138146

139147
#ifdef LODEPNG_COMPILE_DECODER
140148
/* Safely check if multiplying two integers will overflow (no undefined
@@ -144,13 +152,6 @@ static int lodepng_mulofl(size_t a, size_t b, size_t* result) {
144152
return (a != 0 && *result / a != b);
145153
}
146154

147-
/* Safely check if adding two integers will overflow (no undefined
148-
behavior, compiler removing the code, etc...) and output result. */
149-
static int lodepng_addofl(size_t a, size_t b, size_t* result) {
150-
*result = a + b; /* Unsigned addition is well defined and safe in C90 */
151-
return *result < a;
152-
}
153-
154155
#ifdef LODEPNG_COMPILE_ZLIB
155156
/* Safely check if a + b > c, even if overflow could happen. */
156157
static int lodepng_gtofl(size_t a, size_t b, size_t c) {
@@ -2515,50 +2516,61 @@ void lodepng_chunk_generate_crc(unsigned char* chunk) {
25152516
lodepng_set32bitInt(chunk + 8 + length, CRC);
25162517
}
25172518

2518-
unsigned char* lodepng_chunk_next(unsigned char* chunk) {
2519+
unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end) {
2520+
if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
25192521
if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
25202522
&& chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
25212523
/* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
25222524
return chunk + 8;
25232525
} else {
2524-
unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
2525-
return chunk + total_chunk_length;
2526+
size_t total_chunk_length;
2527+
unsigned char* result;
2528+
if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
2529+
result = chunk + total_chunk_length;
2530+
if(result < chunk) return end; /*pointer overflow*/
2531+
return result;
25262532
}
25272533
}
25282534

2529-
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) {
2535+
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) {
2536+
if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
25302537
if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
25312538
&& chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
25322539
/* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
25332540
return chunk + 8;
25342541
} else {
2535-
unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
2536-
return chunk + total_chunk_length;
2542+
size_t total_chunk_length;
2543+
const unsigned char* result;
2544+
if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
2545+
result = chunk + total_chunk_length;
2546+
if(result < chunk) return end; /*pointer overflow*/
2547+
return result;
25372548
}
25382549
}
25392550

2540-
unsigned char* lodepng_chunk_find(unsigned char* chunk, const unsigned char* end, const char type[5]) {
2551+
unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]) {
25412552
for(;;) {
2542-
if(chunk + 12 >= end) return 0;
2553+
if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
25432554
if(lodepng_chunk_type_equals(chunk, type)) return chunk;
2544-
chunk = lodepng_chunk_next(chunk);
2555+
chunk = lodepng_chunk_next(chunk, end);
25452556
}
25462557
}
25472558

25482559
const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) {
25492560
for(;;) {
2550-
if(chunk + 12 >= end) return 0;
2561+
if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
25512562
if(lodepng_chunk_type_equals(chunk, type)) return chunk;
2552-
chunk = lodepng_chunk_next_const(chunk);
2563+
chunk = lodepng_chunk_next_const(chunk, end);
25532564
}
25542565
}
25552566

25562567
unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) {
25572568
unsigned i;
2558-
unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12;
2569+
size_t total_chunk_length, new_length;
25592570
unsigned char *chunk_start, *new_buffer;
2560-
size_t new_length = (*outlength) + total_chunk_length;
2561-
if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/
2571+
2572+
if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77;
2573+
if(lodepng_addofl(*outlength, total_chunk_length, &new_length)) return 77;
25622574

25632575
new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
25642576
if(!new_buffer) return 83; /*alloc fail*/
@@ -2575,8 +2587,9 @@ unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned l
25752587
const char* type, const unsigned char* data) {
25762588
unsigned i;
25772589
unsigned char *chunk, *new_buffer;
2578-
size_t new_length = (*outlength) + length + 12;
2579-
if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/
2590+
size_t new_length = *outlength;
2591+
if(lodepng_addofl(new_length, length, &new_length)) return 77;
2592+
if(lodepng_addofl(new_length, 12, &new_length)) return 77;
25802593
new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
25812594
if(!new_buffer) return 83; /*alloc fail*/
25822595
(*out) = new_buffer;
@@ -4868,7 +4881,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
48684881
if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
48694882
}
48704883

4871-
if(!IEND) chunk = lodepng_chunk_next_const(chunk);
4884+
if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
48724885
}
48734886

48744887
if (state->info_png.color.colortype == LCT_PALETTE
@@ -5750,7 +5763,7 @@ static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t data
57505763
while((size_t)(inchunk - data) < datasize) {
57515764
CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk));
57525765
out->allocsize = out->size; /*fix the allocsize again*/
5753-
inchunk = lodepng_chunk_next(inchunk);
5766+
inchunk = lodepng_chunk_next(inchunk, data + datasize);
57545767
}
57555768
return 0;
57565769
}

lodepng.h

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
LodePNG version 20191219
2+
LodePNG version 20200112
33
4-
Copyright (c) 2005-2019 Lode Vandevenne
4+
Copyright (c) 2005-2020 Lode Vandevenne
55
66
This software is provided 'as-is', without any express or implied
77
warranty. In no event will the authors be held liable for any damages
@@ -856,16 +856,16 @@ Input must be at the beginning of a chunk (result of a previous lodepng_chunk_ne
856856
or the 8th byte of a PNG file which always has the first chunk), or alternatively may
857857
point to the first byte of the PNG file (which is not a chunk but the magic header, the
858858
function will then skip over it and return the first real chunk).
859-
Expects at least 8 readable bytes of memory in the input pointer.
860-
Will output pointer to the start of the next chunk or the end of the file if there
861-
is no more chunk after this. Start this process at the 8th byte of the PNG file.
859+
Will output pointer to the start of the next chunk, or at or beyond end of the file if there
860+
is no more chunk after this or possibly if the chunk is corrupt.
861+
Start this process at the 8th byte of the PNG file.
862862
In a non-corrupt PNG file, the last chunk should have name "IEND".
863863
*/
864-
unsigned char* lodepng_chunk_next(unsigned char* chunk);
865-
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk);
864+
unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end);
865+
const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end);
866866

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

871871
/*
@@ -1775,14 +1775,17 @@ symbol.
17751775
Not all changes are listed here, the commit history in github lists more:
17761776
https://github.com/lvandeve/lodepng
17771777
1778+
*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
1779+
overflow checks.
17781780
*) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables.
1779-
*) 15 jun 2019 (!): auto_choose_color API changed (for bugfix: don't use palette
1780-
if gray ICC profile) and non-ICC LodePNGColorProfile renamed to LodePNGColorStats.
1781+
*) 15 jun 2019: (!) auto_choose_color API changed (for bugfix: don't use palette
1782+
if gray ICC profile) and non-ICC LodePNGColorProfile renamed to
1783+
LodePNGColorStats.
17811784
*) 30 dec 2018: code style changes only: removed newlines before opening braces.
17821785
*) 10 sep 2018: added way to inspect metadata chunks without full decoding.
1783-
*) 19 aug 2018 (!): fixed color mode bKGD is encoded with and made it use
1786+
*) 19 aug 2018: (!) fixed color mode bKGD is encoded with and made it use
17841787
palette index in case of palette.
1785-
*) 10 aug 2018 (!): added support for gAMA, cHRM, sRGB and iCCP chunks. This
1788+
*) 10 aug 2018: (!) added support for gAMA, cHRM, sRGB and iCCP chunks. This
17861789
change is backwards compatible unless you relied on unknown_chunks for those.
17871790
*) 11 jun 2018: less restrictive check for pixel size integer overflow
17881791
*) 14 jan 2018: allow optionally ignoring a few more recoverable errors
@@ -1802,25 +1805,25 @@ Not all changes are listed here, the commit history in github lists more:
18021805
*) 22 dec 2013: Power of two windowsize required for optimization.
18031806
*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key.
18041807
*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png).
1805-
*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_"
1808+
*) 11 mar 2013: (!) Bugfix with custom free. Changed from "my" to "lodepng_"
18061809
prefix for the custom allocators and made it possible with a new #define to
18071810
use custom ones in your project without needing to change lodepng's code.
18081811
*) 28 jan 2013: Bugfix with color key.
18091812
*) 27 okt 2012: Tweaks in text chunk keyword length error handling.
1810-
*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode.
1813+
*) 8 okt 2012: (!) Added new filter strategy (entropy) and new auto color mode.
18111814
(no palette). Better deflate tree encoding. New compression tweak settings.
18121815
Faster color conversions while decoding. Some internal cleanups.
18131816
*) 23 sep 2012: Reduced warnings in Visual Studio a little bit.
1814-
*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions
1817+
*) 1 sep 2012: (!) Removed #define's for giving custom (de)compression functions
18151818
and made it work with function pointers instead.
18161819
*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc
18171820
and free functions and toggle #defines from compiler flags. Small fixes.
1818-
*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible.
1819-
*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed
1821+
*) 6 may 2012: (!) Made plugging in custom zlib/deflate functions more flexible.
1822+
*) 22 apr 2012: (!) Made interface more consistent, renaming a lot. Removed
18201823
redundant C++ codec classes. Reduced amount of structs. Everything changed,
18211824
but it is cleaner now imho and functionality remains the same. Also fixed
18221825
several bugs and shrunk the implementation code. Made new samples.
1823-
*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best
1826+
*) 6 nov 2011: (!) By default, the encoder now automatically chooses the best
18241827
PNG color model and bit depth, based on the amount and type of colors of the
18251828
raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color.
18261829
*) 9 okt 2011: simpler hash chain implementation for the encoder.
@@ -1829,7 +1832,7 @@ Not all changes are listed here, the commit history in github lists more:
18291832
A bug with the PNG filtertype heuristic was fixed, so that it chooses much
18301833
better ones (it's quite significant). A setting to do an experimental, slow,
18311834
brute force search for PNG filter types is added.
1832-
*) 17 aug 2011 (!): changed some C zlib related function names.
1835+
*) 17 aug 2011: (!) changed some C zlib related function names.
18331836
*) 16 aug 2011: made the code less wide (max 120 characters per line).
18341837
*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors.
18351838
*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled.
@@ -1937,5 +1940,5 @@ Domain: gmail dot com.
19371940
Account: lode dot vandevenne.
19381941
19391942
1940-
Copyright (c) 2005-2019 Lode Vandevenne
1943+
Copyright (c) 2005-2020 Lode Vandevenne
19411944
*/

lodepng_unittest.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
LodePNG Unit Test
33
4-
Copyright (c) 2005-2019 Lode Vandevenne
4+
Copyright (c) 2005-2020 Lode Vandevenne
55
66
This software is provided 'as-is', without any express or implied
77
warranty. In no event will the authors be held liable for any damages
@@ -1290,13 +1290,14 @@ void createComplexPNG(std::vector<unsigned char>& png) {
12901290

12911291
std::string extractChunkNames(const std::vector<unsigned char>& png) {
12921292
const unsigned char* chunk = &png[8];
1293+
const unsigned char* end = &png.back() + 1;
12931294
char name[5];
12941295
std::string result = "";
12951296
for(;;) {
12961297
lodepng_chunk_type(name, chunk);
12971298
result += (std::string(" ") + name);
12981299
if(std::string(name) == "IEND") break;
1299-
chunk = lodepng_chunk_next_const(chunk);
1300+
chunk = lodepng_chunk_next_const(chunk, end);
13001301
assertTrue(chunk < &png.back(), "jumped out of chunks");
13011302
}
13021303
return result;
@@ -3671,7 +3672,9 @@ int main() {
36713672
doMain();
36723673
}
36733674
catch(...) {
3674-
std::cout << "error!" << std::endl;
3675+
std::cout << std::endl;
3676+
std::cout << "caught error!" << std::endl;
3677+
std::cout << "*** TEST FAILED ***" << std::endl;
36753678
}
36763679

36773680
return 0;

0 commit comments

Comments
 (0)