Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2022-37768: Infinite loop in Frame::ParseTrailer · Issue #77 · thorfdbg/libjpeg

libjpeg commit 281daa9 was discovered to contain an infinite loop via the component Frame::ParseTrailer.

CVE
#ios

version: latest commit 14bf94c
poc: poc.zip
command: ./jpeg poc /dev/null

The backtrace in gdb:

(gdb) bt
#0  ByteStream::Get (this=0x790ae0) at bytestream.cpp:223
#1  0x000000000042331e in IOStream::PeekWord (this=0x790ae0)
    at iostream.cpp:543
#2  0x00000000004c38d5 in Frame::ParseTrailer (this=0x792590, io=0x790ae0)
    at frame.cpp:1018
#3  0x000000000043aac9 in JPEG::ReadInternal (this=0x7904c8,
    tags=0x7fffffffdd50) at jpeg.cpp:332
#4  0x000000000043988b in JPEG::Read (this=0x7904c8, tags=0x7fffffffdd50)
    at jpeg.cpp:210
#5  0x000000000041cabb in Reconstruct (infile=<optimized out>,
    outfile=0x7fffffffe70c "/dev/null", colortrafo=1, alpha=0x0, upsample=true)
    at reconstruct.cpp:121
#6  0x0000000000408b6a in main (argc=<optimized out>, argv=0x790f29)
    at main.cpp:747

Root cause:

There is a loop in frame.cpp:1017-1118.

In line 1018, the program continuously reads marker in the stream by calling IOStream::PeekWord() function.

LONG marker = io->PeekWord();

In cases of the default branch, the loop won’t exit.

if (marker < 0xff00) {

JPG_WARN(MALFORMED_STREAM,"Frame::ParseTrailer",

“expecting a marker or marker segment - stream is out of sync”);

// Advance to the next marker and see how it goes from there…

io->Get(); // Remove the invalid thing.

do {

marker = io->Get();

} while(marker != 0xff && marker != ByteStream::EOF);

//

if (marker == ByteStream::EOF) {

JPG_WARN(UNEXPECTED_EOF,"Frame::ParseTrailer",

“run into an EOF while scanning for the next marker”);

return false;

}

io->LastUnDo();

// Continue parsing, check what the next marker might be.

} else {

// Something that looks like a valid marker. This could be the

// tables/misc section of the next frame or next scan, depending

// on whether we are hierarchical (next frame) or progressive (next scan).

// Unfortunately, what is what we only know after having received either

// the SOS marker (next scan) or an SOF marker (next frame).

// Thus, at this time, parse of the tables, place its data in the

// global table namespace, overriding what was there, then

// continue parsing here until we know what we have.

assert(m_pTables);

// This might include EXP if we are hierarchical.

m_pTables->ParseTables(io,NULL,m_pParent->isHierarchical(),(m_Type == JPEG_LS)?true:false);

}

}

The problem is that the IOStream::PeekWord() function might always return a same value.

Specifically, the IOStream::PeekWord() calls ByteStream::Get().

LONG ByteStream::Get(void) // read a single byte (not inlined)

{

if (m_pucBufPtr >= m_pucBufEnd) {

if (Fill() == 0) // Found EOF

return EOF;

}

assert(m_pucBufPtr < m_pucBufEnd);

return *m_pucBufPtr++;

}

IOStream::PeekWord() returns at line 223 with m_pucBufPtr++. However, when using gdb to check the value of m_pucBufPtr, I found that the ByteStream::Get() functions repeatedly read the values from the same address. The 0x790f2a and 0x790f29 correspond to byte1 and byte2 in IOStream::PeekWord() and they never change.

IOStream::PeekWord() then always returns a same value (calculated by byte1 and byte2). Finally, the program never terminates because the return value forces the program to take the default branch.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda