fix: reject zero-progress packet headers for CVE-2023-39327 - #1624
fix: reject zero-progress packet headers for CVE-2023-39327#1624jeffspahr wants to merge 10 commits into
Conversation
Fail malformed packet header decoding when it makes no progress so the no-EPH issue1472 variant exits cleanly instead of looping forever on SOP warnings. Add focused unit coverage for the no-EPH payload. Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Signed-off-by: Jeff Spahr <spahrj@gmail.com>
|
@mayeut Any opinion on this ? (since it seems to be an area you touched couple years ago) |
|
other candidate fix for same issue in #1616 ... |
| if ((p_tcp->csty & J2K_CP_CSTY_SOP) != 0U) { | ||
| opj_event_msg(p_manager, EVT_ERROR, | ||
| "Packet header decoding made no progress\n"); | ||
| return OPJ_FALSE; | ||
| } |
There was a problem hiding this comment.
If I'm re-reading the description in #1547 correctly, the presence of SOP markers is still optional when (p_tcp->csty & J2K_CP_CSTY_SOP) != 0U, i.e. they are allowed, not required.
I might be missing something but this check implies they're required.
There was a problem hiding this comment.
Good catch. I removed the SOP-specific hard failure in 6e67639 so zero-progress empty packets now always terminate packet iteration as header exhaustion instead of making optional SOP absence fatal. I also added unit coverage using an openjpeg-data tolerated EOF fixture with the SOP bit set but no SOP markers, plus kept the issue1472 variants returning promptly.
I really can't dig much into this. Compared to #1616, this PR does not require a counter & a threshold to tune but adds - what seems to be - proper handling of end of stream which does work with other tests images and without any tuning which sounds good. |
Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Simplify the zero-progress guard to l_header_length == 0: a zero-length empty-packet header can only occur when the packet header source (the codestream remainder, or the PPM/PPT buffer) is exhausted. The previous l_current_data == p_src_data conjunct measured the codestream pointer, which the SOP branch advances before the guard runs, so an exhausted PPM/PPT header buffer went undetected while SOP markers remained in the tile body and the decoder kept fabricating empty packets. Replace the single-use opj_packet_progression_t enum with an OPJ_BOOL out-flag matching the surrounding parameter conventions, drop the redundant re-initializations and early returns in opj_t2_decode_packet and opj_t2_skip_packet (the normal fall-through already yields the same result), hoist the opj_t2_finalize_resno_decoded call to the single loop-exit check, and move the flag declaration to the top of the block to satisfy -Werror=declaration-after-statement. Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Decode through a temporary file and opj_stream_create_default_file_stream like the other unit tests instead of a hand-rolled in-memory stream (the third copy in the tree), fold the three empty message callbacks into one, and drive the three malformed Scod variants from a table with a single scratch buffer. Anchor the fixture Scod search at the SOC marker so incidental 0xff 0x52 byte pairs in JP2 wrapper boxes cannot be patched by mistake, and raise the test timeout to 30s so instrumented (valgrind/sanitizer) runs do not flake; the timeout only needs to catch the pre-fix unbounded loop, which never returned. Signed-off-by: Jeff Spahr <spahrj@gmail.com>
Warn and skip deriving the CVE-2023-39327 Scod variants instead of aborting the whole seed-corpus build if the upstream seed's layout ever changes, and simplify the temporary-directory cleanup to a plain mktemp -d plus trap. Signed-off-by: Jeff Spahr <spahrj@gmail.com>
|
This PR was re-evaluated with Claude Fable 5 (multi-angle review of both the approach and the CVE coverage), and I pushed three follow-up commits addressing everything it found: a8f8d15, f8430c1, e987f67. What changedStronger, simpler exhaustion guard (a8f8d15): the zero-progress check is now The same commit also replaces the single-use Test harness (f8430c1): decodes now go through a temporary file and Seed-corpus script (e987f67): an unexpected Scod byte in the upstream seed now warns and skips the derived variants instead of aborting the entire corpus build, and the temp-dir cleanup is a plain One reviewer suggestion was tested and deliberately not taken: making Why this is the right fix for CVE-2023-39327The root cause is that the bio reader fabricates 0-bits at EOF, so Verification
|
Addresses CVE-2023-39327.
PR #1547 requires EPH markers and fixes the exact
issue1472PoC, but malformed variants derived from the same codestream can still reach the empty-packet path with zero forward progress. WithScod = 0x03(PRT+SOP), the decoder loops printingNot enough space for expected SOP marker. WithScod = 0x01(PRTonly), it can spend excessive time walking packet iteration after EOF.The root cause is that
opj_t2_read_packet_header()can still report a successful empty packet after consuming zero bytes. When that happens, the caller keeps iterating packets against unchanged input.This change makes packet progression explicit in the T2 decode path. A zero-progress empty-packet header is treated as packet-header exhaustion instead of a successful packet, and the decoder finalizes
resno_decodedthe same way the tolerant EOF path would have ended before stopping packet iteration. This stops the malformed inputs promptly while preserving tolerated truncated decodes, including codestreams where SOP markers are allowed byScodbut absent.It also expands the focused unit coverage to exercise three nearby malformed variants derived from the same in-memory codestream:
Scod = 0x03(PRT+SOP)Scod = 0x01(PRTonly)Scod = 0x07(PRT+SOP+EPH)The unit test also covers a tolerated EOF fixture from
openjpeg-dataafter setting the SOP bit inScodwithout adding SOP markers, so optional SOP absence remains non-fatal.To keep the same-CVE fuzz follow-up in this PR,
tests/fuzzers/build_seed_corpus.shnow derives the0x03and0x01issue1472variants from the existing upstreamissue1472-bigloop.j2kseed when buildingopj_decompress_fuzzer_seed_corpus.zip. The script verifies the source seed still has the expectedScod = 0x07byte before mutating it.Testing
opj_decompressandtestissue1472_noephctest -R "^testissue1472_noeph$" --output-on-failureScod = 0x07variant: it fails quickly withNot enough space for required EPH markerScod = 0x03variant: it returns promptly withStream too short, expected SOTScod = 0x01variant: it returns promptly withStream too short, expected SOTMarrin.jp2dwt_interleave_h.gsr105.jp2dwt_interleave_h.gsr105.jp2 -d 1,1,33,33issue226.j2k(same local baseline failure asmaster)Marrin.jp2dwt_interleave_h.gsr105.jp2tests/fuzzers/build_seed_corpus.shagainst the standardopenjpeg-datacheckout and confirmedopj_decompress_fuzzer_seed_corpus.zipcontains:issue1472-bigloop.j2kissue1472-bigloop-noeph.j2kissue1472-bigloop-prt-only.j2k