BUG: Prevent SIGSEGV in VectorImage SetPixel with shorter source VLV#6087
Conversation
|
| Filename | Overview |
|---|---|
| Modules/Core/Common/include/itkDefaultVectorPixelAccessor.h | Fixes SIGSEGV by clamping the copy loop bound to min(m_VectorLength, input.GetSize()); change is minimal, correct, and safe for all existing callers. |
| Modules/Core/Common/test/itkVariableLengthVectorGTest.cxx | Adds two targeted regression tests: one for the crash path (zero-length source VLV) and one for the happy path (matched-length source); both test cases are well-structured and correct. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["it.Set(vlv) called"] --> B["DefaultVectorPixelAccessor::Set"]
B --> C["copyLength = min(m_VectorLength, input.GetSize())"]
C --> D{copyLength == 0?}
D -- "Yes (e.g. VLV(0), m_Data=nullptr)" --> E["No-op: loop never executes\n✅ No SIGSEGV"]
D -- "No (copyLength > 0)" --> F["Loop: truePixel[i] = input[i]\nfor i in [0, copyLength)"]
F --> G{copyLength < m_VectorLength?}
G -- "Yes (shorter source)" --> H["Trailing components left unchanged"]
G -- "No (matched source)" --> I["All m_VectorLength components written\n✅ Normal path preserved"]
Reviews (1): Last reviewed commit: "BUG: Prevent SIGSEGV in VectorImage SetP..." | Re-trigger Greptile
eba68b5 to
712ed64
Compare
Regression introduced by 1d87efa (BUG: Make VariableLengthVector::m_Data null when its length is zero). Before that commit, VariableLengthVector<T>(length) always allocated via new T[length], so even length-0 had a non-null data pointer; DefaultVectorPixelAccessor::Set's fixed-count copy loop 'happened to work'. After that commit, m_Data is nullptr for length-0, and the vectorized memcpy dereferences null -> SIGSEGV. Guard the copy loop in DefaultVectorPixelAccessor::Set to copy min(m_VectorLength, input.GetSize()) components. Short sources become safe no-ops; trailing destination components are left unchanged. Companion regression test VectorImageSetPixelFromShorterSourceIsSafe fails on current main and passes with this change. VectorImageSetPixelFromMatchedSourceWorks verifies the normal copy path. Discovered while hunting a crash in ITKPerformanceBenchmarking's CopyIterationBenchmark.
712ed64 to
af68814
Compare
dzenanz
left a comment
There was a problem hiding this comment.
Fix looks good. I did not take a good look at the regression tests.
For the sake of clarity, I would like to mention that the default-constructor of 21 years ago: ITK/Code/Common/itkVariableLengthVector.txx Lines 26 to 32 in b516cb8 Note that |
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Adds .github/workflows/perf-benchmark.yml — a GitHub Actions workflow that builds ITK twice (merge-base and PR HEAD) with the PerformanceBenchmarking remote module enabled, then drives the ASV harness from InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 against each build to produce an asv compare report. Bumps the PerformanceBenchmarking remote module pin from 7950c1d76095033edbf7601a925cdacc2fe717f9 to 41bf1b9cfbaa1b146e1b3e5d3ad3571b2203ce1e (master HEAD, post-InsightSoftwareConsortium#111). The new pin carries: * asv.conf.json, benchmarks/{core,filtering,segmentation}.py, python/itk_perf_shim/ (the ASV harness). * Hardened examples/Core/itkCopyIterationBenchmark.cxx that no longer crashes on VectorImage under ITK >= 1d87efa (the VLV null-data regression; see InsightSoftwareConsortium#6087 for the parallel ITK-side defensive null-guard). Supersedes the 2019 Azure Pipelines macos-11 approach; macos-11 is retired and the older evaluate-itk-performance.py driver has been replaced by the ASV-native harness. For the very first PR (this one) the merge-base lacks the new pin, so its benchmark run is best-effort and the asv compare step is wrapped with continue-on-error. The workflow becomes fully useful for regression detection after this PR lands.
Summary
Restore the ability to pass an under-sized
itk::VariableLengthVector(including a length-0 one) as the source of aVectorImagepixelSetwithout crashing. Regression introduced by commit1d87efa52("BUG: MakeVariableLengthVector::m_Datanull when its length is zero").Root cause
Before
1d87efa52,VariableLengthVector<T>(length)always allocated vianew T[length], so even a length-0 vector held a non-null (though zero-size) data pointer.DefaultVectorPixelAccessor::Setcopies exactlym_VectorLengthcomponents from that source pointer:A source shorter than
m_VectorLengthwas a latent caller bug, but "happened to work" because the non-null (out-of-spec) read did not crash. After1d87efa52, a length-0 VLV hasm_Data == nullptr, and the compiler-vectorized fixed-count copy becomes amemcpy(dst, nullptr, N*sizeof(T))— SIGSEGV.Reproducer (register state captured at crash):
Fix
Guard the copy loop in
DefaultVectorPixelAccessor::Setso that at mostmin(m_VectorLength, input.GetSize())components are copied. When the source is shorter, the trailing destination components are left unchanged. A length-0 source is now a safe no-op.This preserves
1d87efa52's null-data intent for zero-length VLVs without requiring every internal copy path to check for null before dereferencing.Regression test
Two new cases in
itkVariableLengthVectorGTest.cxx:VectorImageSetPixelFromShorterSourceIsSafe— constructs aVLV<float>(0), sets it into a 3-componentVectorImage<float,3>pixel via the iterator. Fails with SIGSEGV on current main, passes with this change.VectorImageSetPixelFromMatchedSourceWorks— the normal length-matched path; verifies the copy still works and round-trips.How it was discovered
Hunting a SIGSEGV in
InsightSoftwareConsortium/ITKPerformanceBenchmarking'sCopyIterationBenchmarkacross the v5.4.5 → current main bisect range. The benchmark passesstatic_cast<PixelType>(count)to seed pixel values — forVectorImage, this calls the VLV length constructor, so the very first iteration produces a length-0 source. Before1d87efa52the benchmark silently worked (reading uninitialized bytes); after, it crashes.The benchmark itself is independently being hardened in InsightSoftwareConsortium/ITKPerformanceBenchmarking#111 so it constructs correctly-sized VLV pixels and works across the full bisect range regardless of this ITK-side fix.
Test plan
VectorImageSetPixelFromShorterSourceIsSafereproducer; fails onmainwithout the fix, passes with itVectorImageSetPixelFromMatchedSourceWorksfor the happy pathITKCommonand re-ranCopyIterationBenchmark— 15/15 probes captured, rc=0