IiqDecoder: bounds-check col before correctBadColumn#965
Conversation
| if (col < 2 || col + 2 >= mRaw->dim.x) { | ||
| break; | ||
| } | ||
| correctBadColumn(col); |
There was a problem hiding this comment.
As far as the callsite is concerned, correctBadColumn() is a black-box,
we can't randomly skip it.
|
Thanks for the feedback. You're right that the callsite shouldn't encode knowledge of Would moving the guard inside void IiqDecoder::correctBadColumn(int col) const {
if (col < 2 || col + 2 >= mRaw->dim.x)
return;
// ... rest unchanged ...
}This keeps the callsite clean and makes the function self-contained about what inputs it can handle. Happy to update the PR if this direction looks good. |
I mean, it's not like there is some third place where that check could be placed? :)
|
|
Haha, fair point! Moved the guard inside |
|
@Alearner12 thank you! |
Bug
IiqDecoder::correctSensorDefects(src/librawspeed/decoders/IiqDecoder.cpp:501) only validatescol >= mRaw->dim.xbefore dispatching tocorrectBadColumn(col)for tag types 131 and 137.correctBadColumn(line 530) then reads neighbours atcol-1,col+1,col-2,col+2over a loop that rangesrowin[2, dim.y-3].Two failure modes once
correctBadColumnis entered with a near-edgecol:col0or1img(row-2, col-2)atrow=2data[(0)*W + (col-2)]=data[-2]ordata[-1]— heap OOB read before bufferdim.x-1,dim.x-2img(row+2, col+2)atrow=dim.y-3data[(dim.y-1)*W + (W or W+1)]— heap OOB read past buffer end0img(row-1, col-1)atrow=2data[1*W - 1]— in-bounds wrap to previous row's last element; silent data corruption (not OOB)The
Array2DRef::operator()invariantinvariant(col >= 0)(adt/Array2DRef.h:~67) compiles to__builtin_assumein release builds (NDEBUGdefined; see adt/Invariant.h:36), so the OOB is reached rather than asserted on. ASan flags it cleanly.Reproduction
Standalone extract of the access pattern (no librawspeed dependency, no IIQ file). Mirrors the non-green-path reads at lines 570-572. Build with
g++ -O1 -g -fsanitize=address,undefined -DNDEBUG minimal_poc.cpp -o minimal_poc:Matches the predicted
img(row-2, col-2)atrow=2, col=0=data[-2]— 2 uint16_t elements = 4 bytes before the buffer start.Fix
Caller-side guard, sitting right after the existing
col >= mRaw->dim.xcheck and inside the type-131/137 dispatch arm so the bad-pixel (type 129) path is unaffected.correctBadColumnis a private member with only this one caller, so caller-side and function-side give the same coverage; the caller-side shape matches the existing upper-bound check style and is the smaller diff.Verification
Same access pattern guarded by the equivalent check (
minimal_poc_patched.cpp):All previously-OOB columns now skipped, in-bounds column still processed.
The full librawspeed test suite was not run in this engagement (missing libjpeg/libpugixml dev packages in my environment blocked the full cmake build). The change is additive within the existing
case 131/137:arm; tag types 129 and unknown types, the upper-bound check, and the rest of the loop are unchanged, so no existing-input-path regression is possible by construction.