Bruker 2dseq reader: ParaVision on-disk format conformance - #6761
Bruker 2dseq reader: ParaVision on-disk format conformance#6761gdevenyi wants to merge 6 commits into
Conversation
|
This work is built on https://github.com/gdevenyi/brkraw-legacy/blob/main/FILE_FORMAT.md which was constructed using an extensive AI deep dive into publicly available Bruker datasets, the Bruker Paravision manuals over multiple versions. |
|
@greptileai review this. |
|
| for (int c = 0; c < count; ++c) | ||
| { | ||
| expanded += value; | ||
| expanded += ' '; | ||
| } |
There was a problem hiding this comment.
Unbounded RLE expansion exhausts resources
A crafted Bruker parameter record such as @2147483647*(2) reaches this loop while ReadImageInformation() parses visu_pars. The file-controlled repetition count is accepted by std::stoi and drives one append per repetition with no count or expanded-output-size limit, so parsing attempts to allocate roughly 4 GiB for this small input and can terminate the process or consume excessive CPU. Reject oversized counts and enforce checked bounds on the total expanded output before expanding RLE values.
Artifacts
Focused Bruker RLE resource-limit harness source
- This authored C++ harness copies the inspected `ExpandRLE` implementation unchanged and invokes it with normal and malicious RLE values under a controlled address-space limit, showing the vulnerable loop can be exercised safely.
- The recorded g++ command compiled the focused RLE harness successfully with exit code 0, showing the reproduction executable was built.
Normal Bruker RLE expansion output
- The recorded normal-input run expanded `@12*(2)` to 24 bytes and exited successfully, establishing the baseline behavior.
Malicious Bruker RLE expansion under memory limit
- The recorded resource-limited malicious-input run processed `@2147483647*(2)` until `std::bad_alloc`, demonstrating attempted unbounded allocation from the file-controlled count.
- This source capture records the exact inspected implementation, including the unbounded count-controlled append loop, tying the harness to the reviewed file.
Bruker header parsing call path
- This source capture shows `ParseJCAMPDXRecord` calls `ExpandRLE` and `ReadImageInformation` reads `visu_pars`, establishing that the issue occurs while reading image information.
- This recorded environment check found no CMake cache or ITK configuration, documenting why a fully linked ITK reader execution was unavailable.
There was a problem hiding this comment.
Confirmed. Fixed in bc6224d: ExpandRLE now rejects repetition counts longer than nine digits and throws once the expanded record would exceed 64 MiB, so the crafted @2147483647*(2) case fails with a clean itk::ExceptionObject instead of attempting the allocation. Covered by the new Bruker2dseqImageIO.RejectOversizedRLEExpansion GTest.
PV5.1 headers have two $$ lines, not three, which desynchronized the fixed-header parse. ParaVision 360 files add run-length encoded arrays (@n*(value)), $$ comments inside wrapped value blocks, commas inside <> strings, enum values stored as sized arrays, and scalar struct values on the parameter line. Parse records by their layout instead of assuming a fixed header and comma-splittable structs. Change-Id: I20430e5c667057d20920a9ad9b3c7a85163eb808
VisuCoreDataSlope and VisuCoreDataOffs may be absent, hold a single value applying to every frame, or hold one value per frame; indexing them per-frame read out of bounds when a single value was stored. Change-Id: I9ad4eebf2d9316a0557c93261cd9d2db6f178b36
2D datasets without an FG_SLICE frame group (FG_ISA parameter maps) are single slice; deriving the slice count from the identical per-frame positions gave a zero slice spacing. Orient the slice axis along the actual slice-position step so oblique and coronal stacks match their stored geometry. Change-Id: If0dce54d2d8ebd770e85801be8e9d889626521f5
Change-Id: I6caa1598d92f3a51dc4002520cb064aec3ae26bf
KWStyle reports "{ value };" initializers as an unnecessary
semicolon, failing ITKIOBrukerKWStyleTest.
Change-Id: Id2fa7f90374d2b9043413909640517530d524b2a
A crafted repetition count such as @2147483647*(2) in visu_pars drove a multi-GiB allocation while reading image information. Reject counts of more than nine digits and expansions past 64 MiB. Change-Id: I4c0c1e90045173c439febad47f9a40e225f87433
350c966 to
bc6224d
Compare
Parse the JCAMP-DX forms ParaVision writes (PV5.1 headers, PV360 RLE and enum arrays, strings with commas), fix frame-scaling cardinality, and derive slice count and direction from frame groups. Adds a synthetic PV360 GTest, no new external test data.
Defects fixed, per commit
##/three-$$header assumption desynchronized on PV5.1 files (two$$lines —VisuVersionwas silently consumed) and could not read ParaVision 360 files at all:@N*(value)run-length encoded arrays,$$ @vis=comments inside wrapped value blocks, commas inside<>strings (e.g.<Parameter maps T2 relaxation, bg: Otsu.>), enum values stored as sized arrays (( 1 )+disk_normal_slice_order), and scalar struct values on the parameter line.VisuCoreDataSlope/VisuCoreDataOffsmay hold one value for all frames or one per frame; per-frame indexing read out of bounds when a single value was stored.FG_SLICE(e.g.FG_ISAparameter maps) are single-slice; deriving the slice count from identical per-frame positions produced a zero slice spacing. The slice axis now follows the sign of the slice-position step along the orientation's third row, generalizing the previous coronal-only Y-component heuristic to oblique stacks.Test results
itkBruker2dseq_PV5.1_FSE_INT16/PV6.0_FLASH_*regression tests pass with unchanged baselines.Bruker2dseqImageIO.ReadParaVision360DatasetGTest covers RLE arrays, wrapped strings with embedded commas, mid-value comments, broadcast scaling, frame-group reordering, and a reversed slice axis.VisuCorePositionprogression.AI assistance