Skip to content

Decoders: use 1ULL for whitePoint shift to avoid UB at bps=32#966

Closed
Alearner12 wants to merge 1 commit into
darktable-org:developfrom
Alearner12:fix-shift-ub
Closed

Decoders: use 1ULL for whitePoint shift to avoid UB at bps=32#966
Alearner12 wants to merge 1 commit into
darktable-org:developfrom
Alearner12:fix-shift-ub

Conversation

@Alearner12
Copy link
Copy Markdown

Bug

Identified a potential undefined behavior (UB) in several decoders where a 32-bit shift is performed on a value that can be 32, specifically when calculating the default white level:

  • src/librawspeed/decoders/DngDecoder.cpp:377
  • src/librawspeed/decoders/RafDecoder.cpp:292
  • src/librawspeed/decoders/RawDecoder.cpp:124
mRaw->whitePoint = implicit_cast<int>((1UL << bits) - 1UL);

The bit depth (bits) is permitted to be 32 (e.g., in DNG). 1UL << 32 is undefined per C++17 [expr.shift]/1 on platforms where sizeof(unsigned long) == 4: Windows MSVC (LLP64) and 32-bit Linux/macOS (ILP32).

Reproduction

Standalone extract built with gcc -O1 -g -fsanitize=undefined. To make the UB visible regardless of host sizeof(long), the PoC runs the equivalent shift on an explicit uint32_t:

runtime error: shift exponent 32 is too large for 32-bit type 'unsigned int'
bps= 1: uint32_t shift -> 1, unsigned long shift -> 1
bps= 8: uint32_t shift -> 255, unsigned long shift -> 255
bps=16: uint32_t shift -> 65535, unsigned long shift -> 65535
bps=24: uint32_t shift -> 16777215, unsigned long shift -> 16777215
bps=31: uint32_t shift -> 2147483647, unsigned long shift -> 2147483647
bps=32: uint32_t shift -> 0, unsigned long shift -> -1

On a 32-bit target or Windows MSVC, the unsigned long expression is UB.

Fix

Switched from 1UL to 1ULL (unsigned long long) for the shift calculation in DngDecoder, RafDecoder, and RawDecoder. unsigned long long is guaranteed to be at least 64 bits per C99, making 1ULL << 32 well-defined on every platform.

Verification

Verified with the patched PoC; no sanitizer errors and correct results for all bit depths:

bps= 1: uint32_t shift -> 1, unsigned long long shift -> 1
bps= 8: uint32_t shift -> 255, unsigned long long shift -> 255
bps=16: uint32_t shift -> 65535, unsigned long long shift -> 65535
bps=24: uint32_t shift -> 16777215, unsigned long long shift -> 16777215
bps=31: uint32_t shift -> 2147483647, unsigned long long shift -> 2147483647
bps=32: uint32_t shift -> -1, unsigned long long shift -> -1

All bits < 32 cases produce identical values to the unpatched version. bits == 32 now portably produces -1 (0xFFFFFFFF).

@Alearner12 Alearner12 requested a review from LebedevRI as a code owner May 25, 2026 18:58
@LebedevRI
Copy link
Copy Markdown
Member

@Alearner12 please refrain from submitting AI womit to this repo.

@LebedevRI LebedevRI closed this May 25, 2026
@Alearner12
Copy link
Copy Markdown
Author

Noted. Though the write-up style in #965 was similar and you merged that is the issue the style, or is the bug here not valid? make sure I won't repeat it for future contributions.

@LebedevRI
Copy link
Copy Markdown
Member

LebedevRI commented May 25, 2026

Noted. Though the write-up style in #965 was similar and you merged that is the issue the style, or is the bug here not valid? make sure I won't repeat it for future contributions.

This is an AI hallucination.
In two of three changed functions,
the check that bps <= 16 is done earlier in the same function,
in DNG, the check is done in the caller function.

@Alearner12
Copy link
Copy Markdown
Author

Sorry for the noise. I missed the tighter gate that caps the shift count at ≤16 stopped at the first *bps > 32 check in DngDecoder without grepping forward. The UB doesn't exist.

@LebedevRI
Copy link
Copy Markdown
Member

Please don't misunderstand me, i understand that no harm was intended here.
I'm just vary of all the ongoing AI slopification.

@Alearner12
Copy link
Copy Markdown
Author

Appreciated. And you're right to push back submitting without deep verification wastes your time on bugs that don't exist. I'll be more thorough before opening a PR.

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

Successfully merging this pull request may close these issues.

2 participants