Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite loop in JPEG::ReadInternal #76

Closed
0xdd96 opened this issue Jun 26, 2022 · 1 comment
Closed

Infinite loop in JPEG::ReadInternal #76

0xdd96 opened this issue Jun 26, 2022 · 1 comment

Comments

@0xdd96
Copy link

0xdd96 commented Jun 26, 2022

version: latest commit 842c7ba
poc: poc
command: ./jpeg poc /dev/null

Here is the backtrace in GDB:

pwndbg> backtrace
#0  0x000055555558fa8a in Image::StartParseFrame (this=0x5555557433a0, io=0x555555741ad0) at image.cpp:658
#1  0x0000555555584739 in JPEG::ReadInternal (this=0x5555557414b8, tags=0x7fffffffdcf0) at jpeg.cpp:286
#2  0x00005555555843de in JPEG::Read (this=0x5555557414b8, tags=0x7fffffffdcf0) at jpeg.cpp:210
#3  0x000055555557a23e in Reconstruct (infile=0x7fffffffe6cf "../../script/test-libjpeg/modify8", outfile=0x7fffffffe6f1 "/dev/null", colortrafo=1, alpha=0x0, upsample=true) at reconstruct.cpp:121
#4  0x00005555555715be in main (argc=3, argv=0x7fffffffe448) at main.cpp:747
#5  0x00007ffff7abf0b3 in __libc_start_main (main=0x55555556fac1 <main(int, char**)>, argc=3, argv=0x7fffffffe448, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe438) at ../csu/libc-start.c:308
#6  0x000055555556f5ee in _start ()

When marker==0xffd9, ParseFrameHeader will return NULL (line 627, image.cpp), which initializes m_pCurrent with NULL (line 667, image.cpp).

marker = io->GetWord();
switch(marker) {
case ByteStream::EOF:
JPG_THROW(MALFORMED_STREAM,"Image::ParseFrameHeader","unexpected EOF while parsing the image");
break;
case 0xffd9: // EOI
return NULL;

class Frame *Image::StartParseFrame(class ByteStream *io)
{
//
// This should only be called from the main image.
assert(m_pParent == NULL && m_pMaster == NULL);
//
// Check whether we have the frame header. Residual and alpha
// already parse that off as part of ParseTrailer().
if (m_bReceivedFrameHeader == false) {
assert(m_pTables);
m_pCurrent = ParseFrameHeader(io);
// Create the checksum if it is needed.
CreateChecksumWhenNeeded(m_pChecksum);
//
// Is now there.
m_bReceivedFrameHeader = true;
}
//
// Otherwise, the frame header has already been parsed off and need not to be
// looked at here again.
return m_pCurrent;
}

Since m_bReceivedFrameHeadere is set to true after that (lne 672, image.cpp), further calls to StartParseFrame will keep returning m_pCurreent=NULL.

libjpeg/interface/jpeg.cpp

Lines 284 to 353 in 842c7ba

while(m_bDecoding) {
if (m_pFrame == NULL) {
m_pFrame = m_pImage->StartParseFrame(m_pIOStream);
if (m_pFrame) {
m_pDecoder->ParseTags(tags);
if (stopflags & JPGFLAG_DECODER_STOP_FRAME)
return;
}
}
if (m_pFrame) {
while (m_pScan == NULL) {
m_pScan = m_pFrame->StartParseScan(m_pImage->InputStreamOf(m_pIOStream),m_pImage->ChecksumOf());
//
if (m_pScan == NULL) {
// This is not yet the start of the scan, but might
// either be a frame trailer, or part of the frame header.
if (m_pFrame->isEndOfFrame()) {
if (!m_pFrame->ParseTrailer(m_pImage->InputStreamOf(m_pIOStream))) {
// Frame done, advance to the next frame.
m_pFrame = NULL;
if (!m_pImage->ParseTrailer(m_pIOStream)) {
// Image done, stop decoding, image is now loaded.
StopDecoding();
return;
}
}
} else {
if (stopflags & JPGFLAG_DECODER_STOP_FRAME)
return;
}
// else continue looking for the start of scan.
} else {
if (stopflags & JPGFLAG_DECODER_STOP_SCAN)
return;
}
}
if (m_pScan) {
if (m_bRow == false) {
m_bRow = m_pScan->StartMCURow();
if (m_bRow) {
if (stopflags & JPGFLAG_DECODER_STOP_ROW)
return;
} else {
// Scan done, advance to the next scan.
m_pFrame->EndParseScan();
m_pScan = NULL;
if (!m_pFrame->ParseTrailer(m_pImage->InputStreamOf(m_pIOStream))) {
// Frame done, advance to the next frame.
m_pFrame = NULL;
if (!m_pImage->ParseTrailer(m_pIOStream)) {
// Image done, stop decoding, image is now loaded.
StopDecoding();
return;
}
}
}
}
if (m_bRow) {
while (m_pScan->ParseMCU()) {
if (stopflags & JPGFLAG_DECODER_STOP_MCU)
return;
}
m_bRow = false;
}
}
}
}

Such behavior leads to an infinite loop in JPEG::ReadInternal. When m_pFrame==NULL (line 285, jpeg.cpp), it will invoke m_pImage->StartParseFrame to initialize it (line 286, jpeg.cpp). Since StartParseFrame keeps returning NULL in this case, the while loop from line 284-353 cannot terminate.

@thorfdbg
Copy link
Owner

This was fixed in the latest release. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants