Skip to content

NDPluginROIStat: return after rank guard to prevent heap OOB on 3-D (RGB) arrays#594

Merged
MarkRivers merged 1 commit into
areaDetector:masterfrom
physwkim:fix/roistat-rgb-oob
Jul 16, 2026
Merged

NDPluginROIStat: return after rank guard to prevent heap OOB on 3-D (RGB) arrays#594
MarkRivers merged 1 commit into
areaDetector:masterfrom
physwkim:fix/roistat-rgb-oob

Conversation

@physwkim

Copy link
Copy Markdown
Contributor

The "dimensions must be 1 or 2" guard in processCallbacks() only warns and falls through to the geometry loop for (dim=0; dim<pArray->ndims; dim++), which writes offset/size/arraySize[dim]. NDROI_t sizes those to 2 elements, so any ndims==3 frame — every RGB1/2/3 colour image — writes index 2, past each array and, for the last ROI, past the new NDROI[maxROIs_] allocation: a heap out-of-bounds write reachable from an ordinary colour-detector setup, with corrupt stats and a likely crash in the following delete[]. Bail out of the callback (freeing pROIs, leaving the mutex locked per the exit contract), matching the identical guard in NDPluginTimeSeries which already returns. Proven with an AddressSanitizer driver: the buggy path reports a heap-buffer-overflow WRITE of size 8 at the arraySize[dim] store; the fixed path returns cleanly.

…rrays

The "number of array dimensions must be 1 or 2" guard in
processCallbacks() only printed a warning and fell through to the
geometry loop:

    for (dim=0; dim<pArray->ndims; dim++) {
      pROI->offset[dim]  = ...;
      pROI->size[dim]    = ...;
      pROI->arraySize[dim] = ...;
    }

NDROI_t sizes offset[], size[] and arraySize[] to two elements each, so
for any array with ndims == 3 -- which includes every NDColorModeRGB1/2/3
frame -- dim reaches 2 and the loop writes index 2 of each 2-element
array. arraySize is the struct's last member, so arraySize[2] is written
8 bytes past the NDROI object, and for the last ROI past the
new NDROI[maxROIs_] allocation: a heap out-of-bounds write reachable from
an ordinary colour-detector configuration, with corrupted stats and a
likely crash in the delete[] that follows.

Bail out of processCallbacks() (releasing the pROIs allocation and
leaving the mutex locked, as the exit contract requires) instead of
merely warning, matching the identical guard in NDPluginTimeSeries which
already returns.

Verified with an AddressSanitizer proof driver mirroring the NDROI_t
layout and the dim loop: a 3-D array triggers a heap-buffer-overflow
WRITE of size 8 without the return, and returns cleanly with it.
@MarkRivers

Copy link
Copy Markdown
Member

@physwkim thanks for finding all of the bugs in ADCore and asyn.

I am curious how you did this. Did you have an AI agent study the code? If so, that seems very useful.

@MarkRivers
MarkRivers merged commit 16624ed into areaDetector:master Jul 16, 2026
9 checks passed
@physwkim

Copy link
Copy Markdown
Contributor Author

@physwkim thanks for finding all of the bugs in ADCore and asyn.

I am curious how you did this. Did you have an AI agent study the code? If so, that seems very useful.

@MarkRivers Come across this while working on epics-rs, a Rust port of EPICS - spotted the upstream bug during the port and reported it.

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