Skip to content

Commit 9652b36

Browse files
committed
Fix decoding bug in bit reader
This bug could cause a wrong numeric value reading a PNG in the rare case of long huffman symbol for a distance with many extra bits Also add tests for the bit reader
1 parent 2e541f5 commit 9652b36

File tree

3 files changed

+141
-10
lines changed

3 files changed

+141
-10
lines changed

lodepng.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
LodePNG version 20191109
2+
LodePNG version 20191219
33
44
Copyright (c) 2005-2019 Lode Vandevenne
55
@@ -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 = "20191109";
47+
const char* LODEPNG_VERSION_STRING = "20191219";
4848

4949
/*
5050
This source file is built up in the following large parts. The code sections
@@ -535,7 +535,7 @@ static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) {
535535
reader->buffer = 0;
536536
if(start + 0u < size) reader->buffer |= reader->data[start + 0];
537537
reader->buffer >>= (reader->bp & 7u);
538-
return reader->bp + nbits < reader->bitsize;
538+
return reader->bp + nbits <= reader->bitsize;
539539
}
540540
}
541541

@@ -553,7 +553,7 @@ static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) {
553553
if(start + 0u < size) reader->buffer |= reader->data[start + 0];
554554
if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
555555
reader->buffer >>= (reader->bp & 7u);
556-
return reader->bp + nbits < reader->bitsize;
556+
return reader->bp + nbits <= reader->bitsize;
557557
}
558558
}
559559

@@ -572,7 +572,7 @@ static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbi
572572
if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
573573
if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
574574
reader->buffer >>= (reader->bp & 7u);
575-
return reader->bp + nbits < reader->bitsize;
575+
return reader->bp + nbits <= reader->bitsize;
576576
}
577577
}
578578

@@ -584,7 +584,7 @@ static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbi
584584
reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
585585
((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
586586
reader->buffer >>= (reader->bp & 7u);
587-
reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (7u - (reader->bp & 7u)));
587+
reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u)));
588588
return 1;
589589
} else {
590590
reader->buffer = 0;
@@ -593,12 +593,13 @@ static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbi
593593
if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
594594
if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u);
595595
reader->buffer >>= (reader->bp & 7u);
596-
return reader->bp + nbits < reader->bitsize;
596+
return reader->bp + nbits <= reader->bitsize;
597597
}
598598
}
599599

600-
/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits */
600+
/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */
601601
static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) {
602+
/* The shift allows nbits to be only up to 31. */
602603
return reader->buffer & ((1u << nbits) - 1u);
603604
}
604605

@@ -614,6 +615,25 @@ static unsigned readBits(LodePNGBitReader* reader, size_t nbits) {
614615
advanceBits(reader, nbits);
615616
return result;
616617
}
618+
619+
/* Public for testing only. steps and result must have numsteps values. */
620+
unsigned lode_png_test_bitreader(const unsigned char* data, size_t size,
621+
size_t numsteps, const size_t* steps, unsigned* result) {
622+
size_t i;
623+
LodePNGBitReader reader;
624+
LodePNGBitReader_init(&reader, data, size);
625+
for(i = 0; i < numsteps; i++) {
626+
size_t step = steps[i];
627+
unsigned ok;
628+
if(step > 25) ok = ensureBits32(&reader, step);
629+
else if(step > 17) ok = ensureBits25(&reader, step);
630+
else if(step > 9) ok = ensureBits17(&reader, step);
631+
else ok = ensureBits9(&reader, step);
632+
if(!ok) return 0;
633+
result[i] = readBits(&reader, step);
634+
}
635+
return 1;
636+
}
617637
#endif /*LODEPNG_COMPILE_DECODER*/
618638

619639
static unsigned reverseBits(unsigned bits, unsigned num) {

lodepng.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
LodePNG version 20191109
2+
LodePNG version 20191219
33
44
Copyright (c) 2005-2019 Lode Vandevenne
55

lodepng_unittest.cpp

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3504,6 +3504,117 @@ void testErrorImages() {
35043504
testBase64Image("iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAgMAAAAhHED1AAAAU0lEQVR4Ae3MwQAAAAxFoXnM3/NDvGsBdB8JBAKBQCAQCAQCgUAgEAgEAoFAIBAIBAKBQCAQCAQCgUAgEAgEAoFAIBAIBAKBQCAQCAQCgUAgEEQDHGPAW1eyhK0AAAAASUVORK5CYII=", true, 256, 256, "");
35053505
}
35063506

3507+
// defined in lodepng.cpp
3508+
unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, size_t numsteps, const size_t* steps, unsigned* result);
3509+
3510+
void testBitReaderCase(const std::string& bits, const std::vector<size_t>& steps, bool expect_error, bool silent = false) {
3511+
if(!silent) std::cout << "testBitReaderCase: " << bits << ", #steps: " << steps.size() << std::endl;
3512+
std::vector<unsigned char> data;
3513+
std::vector<bool> bits0;
3514+
size_t bitcount = 0;
3515+
for(size_t i = 0; i < bits.size(); i++) {
3516+
char c = bits[i];
3517+
if(c != '0' && c != '1') continue;
3518+
if((bitcount & 7) == 0) data.push_back(0);
3519+
int bit = (c == '1');
3520+
data.back() |= (bit << (bitcount & 7));
3521+
bits0.push_back(bit);
3522+
bitcount++;
3523+
}
3524+
std::vector<unsigned> result(steps.size());
3525+
unsigned ok = lode_png_test_bitreader(data.data(), data.size(), steps.size(), steps.data(), result.data());
3526+
if(expect_error) {
3527+
assertEquals(0, ok, "expected it would give an error");
3528+
return;
3529+
}
3530+
assertEquals(1, ok, "expected there would be no error");
3531+
3532+
std::vector<bool> bits1;
3533+
for(size_t i = 0; i < steps.size(); i++) {
3534+
size_t step = steps[i];
3535+
size_t value = result[i];
3536+
for(size_t j = 0; j < step; j++) {
3537+
bits1.push_back((value >> j) & 1);
3538+
}
3539+
}
3540+
bits0.resize(bits1.size()); // only test those bits that were actually read for the test
3541+
3542+
assertEquals(bits0, bits1);
3543+
}
3544+
3545+
// This is still using C++98 for lodepng for compatibility, even though I'd love to use C++11 vector initializer lists here.
3546+
// TODO: use modern C++ at least for the unit test
3547+
#define V(arr) std::vector<size_t>(arr, arr + sizeof(arr) / sizeof(*arr))
3548+
3549+
void testBitReader() {
3550+
std::string zeros = "0000000000000000000000000000000000000000000000000000000000000000";
3551+
testBitReaderCase("", std::vector<size_t>(0), false);
3552+
{ size_t arr[] = {1}; testBitReaderCase("0", V(arr), false); }
3553+
{ size_t arr[] = {1}; testBitReaderCase("1", V(arr), false); }
3554+
{ size_t arr[] = {2}; testBitReaderCase("00", V(arr), false); }
3555+
{ size_t arr[] = {2}; testBitReaderCase("01", V(arr), false); }
3556+
{ size_t arr[] = {2}; testBitReaderCase("10", V(arr), false); }
3557+
{ size_t arr[] = {2}; testBitReaderCase("11", V(arr), false); }
3558+
{ size_t arr[] = {3}; testBitReaderCase("111", V(arr), false); }
3559+
{ size_t arr[] = {4}; testBitReaderCase("1111", V(arr), false); }
3560+
{ size_t arr[] = {8}; testBitReaderCase("11111111", V(arr), false); }
3561+
{ size_t arr[] = {9}; testBitReaderCase("111111111", V(arr), false); }
3562+
{ size_t arr[] = {9}; testBitReaderCase("11111111", V(arr), true); }
3563+
{ size_t arr[] = {16}; testBitReaderCase("1111111111111111", V(arr), false); }
3564+
{ size_t arr[] = {17}; testBitReaderCase("11111111111111111", V(arr), false); }
3565+
{ size_t arr[] = {17}; testBitReaderCase("1111111111111111", V(arr), true); }
3566+
{ size_t arr[] = {24}; testBitReaderCase("111111111111111111111111", V(arr), false); }
3567+
{ size_t arr[] = {25}; testBitReaderCase("1111111111111111111111111", V(arr), false); }
3568+
{ size_t arr[] = {25}; testBitReaderCase("111111111111111111111111", V(arr), true); }
3569+
{ size_t arr[] = {31}; testBitReaderCase("1111111111111111111111111111111", V(arr), false); }
3570+
{ size_t arr[] = {1, 31}; testBitReaderCase("11111111111111111111111111111111", V(arr), false); }
3571+
{ size_t arr[] = {31}; testBitReaderCase("111111111111111111111111", V(arr), true); }
3572+
{ size_t arr[] = {16, 16}; testBitReaderCase("11111111111111111111111111111111", V(arr), false); }
3573+
{ size_t arr[] = {1}; testBitReaderCase("0" + zeros, V(arr), false); }
3574+
{ size_t arr[] = {1}; testBitReaderCase("1" + zeros, V(arr), false); }
3575+
{ size_t arr[] = {2}; testBitReaderCase("00" + zeros, V(arr), false); }
3576+
{ size_t arr[] = {2}; testBitReaderCase("01" + zeros, V(arr), false); }
3577+
{ size_t arr[] = {2}; testBitReaderCase("10" + zeros, V(arr), false); }
3578+
{ size_t arr[] = {2}; testBitReaderCase("11" + zeros, V(arr), false); }
3579+
{ size_t arr[] = {3}; testBitReaderCase("111" + zeros, V(arr), false); }
3580+
{ size_t arr[] = {4}; testBitReaderCase("1111" + zeros, V(arr), false); }
3581+
{ size_t arr[] = {8}; testBitReaderCase("11111111" + zeros, V(arr), false); }
3582+
{ size_t arr[] = {9}; testBitReaderCase("111111111" + zeros, V(arr), false); }
3583+
{ size_t arr[] = {16}; testBitReaderCase("1111111111111111" + zeros, V(arr), false); }
3584+
{ size_t arr[] = {17}; testBitReaderCase("11111111111111111" + zeros, V(arr), false); }
3585+
{ size_t arr[] = {24}; testBitReaderCase("111111111111111111111111" + zeros, V(arr), false); }
3586+
{ size_t arr[] = {25}; testBitReaderCase("1111111111111111111111111" + zeros, V(arr), false); }
3587+
{ size_t arr[] = {31}; testBitReaderCase("1111111111111111111111111111111" + zeros, V(arr), false); }
3588+
{ size_t arr[] = {16, 16}; testBitReaderCase("11111111111111111111111111111111" + zeros, V(arr), false); }
3589+
3590+
// 128 arbitrary bits
3591+
std::string test = "10101011110000101110010010000101000100100000111000010010010010010010111100000100100100100000001010111111110001111010101011011001";
3592+
for(size_t i = 0; i < 32; i++) {
3593+
std::cout << "testBitReader loop " << i << std::endl;
3594+
{ size_t arr[] = {i}; testBitReaderCase(test, V(arr), false, true); }
3595+
{ size_t arr[] = {i, i}; testBitReaderCase(test, V(arr), false, true); }
3596+
{ size_t arr[] = {i, i, i}; testBitReaderCase(test, V(arr), false, true); }
3597+
{ size_t arr[] = {i, i, i, i}; testBitReaderCase(test, V(arr), false, true); }
3598+
{ size_t arr[] = {1, i, i, i, i}; testBitReaderCase(test, V(arr), false, true); }
3599+
{ size_t arr[] = {2, i, i, i, i}; testBitReaderCase(test, V(arr), false, true); }
3600+
{ size_t arr[] = {3, i, i, i, i}; testBitReaderCase(test, V(arr), false, true); }
3601+
{ size_t arr[] = {31, 31, 31, i}; testBitReaderCase(test, V(arr), false, true); }
3602+
{ size_t arr[] = {i, i, i, i, i, i, i, i}; testBitReaderCase(test + test, V(arr), false, true); }
3603+
for(size_t j = 0; j < 32; j++) {
3604+
{ size_t arr[] = {i, j}; testBitReaderCase(test, V(arr), false, true); }
3605+
{ size_t arr[] = {i, j, i, j}; testBitReaderCase(test, V(arr), false, true); }
3606+
{ size_t arr[] = {i, i, j, j}; testBitReaderCase(test, V(arr), false, true); }
3607+
{ size_t arr[] = {31, 31, i, j}; testBitReaderCase(test, V(arr), false, true); }
3608+
}
3609+
}
3610+
{ size_t arr[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; testBitReaderCase(test, V(arr), false); }
3611+
{ size_t arr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3}; testBitReaderCase(test, V(arr), false); }
3612+
{ size_t arr[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,31,1,1,1,1,1,1,1,1,1,1,1}; testBitReaderCase(test, V(arr), false); }
3613+
{ size_t arr[] = {16,1,2,4,7,3,6,28,28,31,1,17,12,1,3,8,3,3,14,21,25,24,1,8,7}; testBitReaderCase(test + test + test, V(arr), false); }
3614+
{ size_t arr[] = {5,7,17,15,6,8,4,5,3,11,1,4,4,8,6,4,5,1,6,5,13,8,18,1,1,8,7,2}; testBitReaderCase(test + test + test, V(arr), false); }
3615+
3616+
}
3617+
35073618
void doMain() {
35083619
//PNG
35093620
testPngSuite();
@@ -3546,8 +3657,8 @@ void doMain() {
35463657
testCustomDeflate();
35473658
testCustomZlibDecompress();
35483659
testCustomInflate();
3660+
testBitReader();
35493661
// TODO: add test for huffman code with exactly 0 and 1 symbols present
3550-
// TODO: add case where ensureBits25 and ensureBits32 do left shift of 24 with value >= 128
35513662

35523663
//lodepng_util
35533664
testChunkUtil();

0 commit comments

Comments
 (0)