Skip to content

Signed integer overflow in opj_j2k_get_tile with crafted tile geometry #1643

@Bin-infinite

Description

@Bin-infinite

This report was generated by AI and manually verified.

Summary

src/lib/openjp2/j2k.c::opj_j2k_get_tile
can perform signed 32-bit arithmetic on tile-derived image coordinates that are
not representable as OPJ_INT32. A crafted J2K codestream with extreme SIZ
marker values makes decoding tile 1 reach a signed integer overflow at
src/lib/openjp2/j2k.c:12497.

The issue was reproduced by running the sanitizer-built opj_decompress
executable against the crafted codestream. UBSan reports signed integer
overflow inside opj_j2k_get_tile.

Affected Code

  • File: src/lib/openjp2/j2k.c
  • Function: opj_j2k_get_tile
  • Public path: opj_decompress -t 1
  • Library dispatch path: opj_get_decoded_tile -> opj_j2k_get_tile
  • Vulnerable operation: signed arithmetic after narrowing tile/image
    coordinates to OPJ_INT32

This is a library-code issue in libopenjp2, confirmed through the normal
opj_decompress executable path.

Impact

A crafted codestream can trigger undefined behavior during tile decoding. In the
validated UBSan build, this is reported as signed integer overflow. In
non-sanitized builds, the overflow can lead to incorrect component dimension
calculations and undefined behavior in the decode path.

Reproduction

PoC and evidence are available under:

Generate the crafted codestream:

python3 row-validation-output/case-008/pocs/generate_case008_poc.py \
  --opj-compress build/bin/opj_compress \
  --out-dir row-validation-output/case-008/inputs

Run:

UBSAN_OPTIONS=halt_on_error=0:print_stacktrace=1 \
  build/bin/opj_decompress \
  -i row-validation-output/case-008/inputs/case008_tile_overflow.j2k \
  -o row-validation-output/case-008/tmp/case008_tile_overflow.pgx \
  -t 1 \
  -quiet

The PoC mutates the SIZ marker of a minimal J2K seed so that:

  • Xsiz = 0xffffffff
  • Ysiz = 1
  • XTsiz = 0x80000000
  • YTsiz = 1
  • XTOsiz = 0
  • YTOsiz = 0

This describes two huge horizontal tiles and makes tile 1 produce an
out-of-range signed coordinate in opj_j2k_get_tile.

Sanitizer Evidence

Representative UBSan output:

/home/kery-qi/yf/openjpeg/src/lib/openjp2/openjpeg.c:628:16: runtime error:
call to function opj_j2k_get_tile through pointer to incorrect function type
    #0 0x7dc5b2508d4f in opj_get_decoded_tile /home/kery-qi/yf/openjpeg/src/lib/openjp2/openjpeg.c:628:16
    #1 0x5ea866ec6c07 in main /home/kery-qi/yf/openjpeg/src/bin/jp2/opj_decompress.c:1601:18

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/kery-qi/yf/openjpeg/src/lib/openjp2/openjpeg.c:628:16

/home/kery-qi/yf/openjpeg/src/lib/openjp2/j2k.c:12497:69: runtime error:
signed integer overflow: 0 - -2147483648 cannot be represented in type 'OPJ_INT32' (aka 'int')
    #0 0x7dc5b2479ced in opj_j2k_get_tile /home/kery-qi/yf/openjpeg/src/lib/openjp2/j2k.c:12497:69
    #1 0x5ea866ec6c07 in main /home/kery-qi/yf/openjpeg/src/bin/jp2/opj_decompress.c:1601:18

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/kery-qi/yf/openjpeg/src/lib/openjp2/j2k.c:12497:69
ERROR -> opj_decompress: failed to decode tile!

The same target overflow was reproduced in repeated runs.

The executable also emits unrelated function-pointer-type UBSan diagnostics in
OpenJPEG dispatch wrappers. Those diagnostics are not used as the confirmation
for this report; the confirming diagnostic is the signed integer overflow in
opj_j2k_get_tile.

Root Cause

The tile geometry allows values at or above the signed 32-bit boundary. During
tile decoding, those values are narrowed to OPJ_INT32 and used in signed
subtraction. For the crafted tile, the computation reaches 0 - -2147483648,
which cannot be represented in OPJ_INT32.

Suggested Fix

Validate tile-derived component bounds before narrowing to signed 32-bit types.
Reject SIZ/tile combinations that cannot be represented by the downstream
component dimension calculations, or perform the intermediate calculations in a
wider checked integer type and only narrow after range validation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions