Summary
Severity: CRITICAL
Category: Integer overflow / out-of-bounds write
Location: src/utility/LAGraph_MMRead.c lines 789, 794, 854
Trigger
A Matrix Market file with an extremely large entry count or dense dimensions, e.g.:
%%MatrixMarket matrix coordinate real general
2 2 18446744073709551615
1 1 1.0
1 2 2.0
Root Cause
nvals, dense array products, and nvals3 are computed in unchecked GrB_Index (uint64) arithmetic:
- Line 854:
nvals3 = nvals + 1 wraps to 0 when nvals == UINT64_MAX.
- Lines 855–857: The allocator receives a zero-sized request, rounds up to one element.
- Line 865: The read loop condition
0 < UINT64_MAX is true, so the loop runs ~2^64 iterations, writing past the end of the one-element buffer from the very second entry.
- Lines 789/794: Dense dimension overflow:
nrows * ncols silently wraps to 0 for e.g. nrows = ncols = 4294967296, causing the function to return a huge but empty matrix.
Proof / Trace
- Header parses
nvals = UINT64_MAX at line 770.
- Line 854:
nvals3 = UINT64_MAX + 1 = 0 (wraps).
- Lines 855–857: allocate capacity for 1 triplet (one element each for
I, J, X).
- Line 865: loop condition
0 < UINT64_MAX is true; loop runs UINT64_MAX iterations.
- First
set_value writes slot 0 (in bounds); second call writes slot 1 past the end of the allocated buffer → heap corruption.
Impact
Heap corruption from a crafted sparse file. Silent data loss from crafted dense dimensions. Any code that reads untrusted Matrix Market files is affected.
Suggested Fix
Guard every size multiplication with a checked helper (like the existing LG_Multiply_size_t). Validate nvals before allocation. Reject inputs where nvals > INT64_MAX given the loop counter is int64_t.
Summary
Severity: CRITICAL
Category: Integer overflow / out-of-bounds write
Location:
src/utility/LAGraph_MMRead.clines 789, 794, 854Trigger
A Matrix Market file with an extremely large entry count or dense dimensions, e.g.:
Root Cause
nvals, dense array products, andnvals3are computed in uncheckedGrB_Index(uint64) arithmetic:nvals3 = nvals + 1wraps to0whennvals == UINT64_MAX.0 < UINT64_MAXis true, so the loop runs ~2^64 iterations, writing past the end of the one-element buffer from the very second entry.nrows * ncolssilently wraps to 0 for e.g.nrows = ncols = 4294967296, causing the function to return a huge but empty matrix.Proof / Trace
nvals = UINT64_MAXat line 770.nvals3 = UINT64_MAX + 1 = 0(wraps).I,J,X).0 < UINT64_MAXis true; loop runsUINT64_MAXiterations.set_valuewrites slot 0 (in bounds); second call writes slot 1 past the end of the allocated buffer → heap corruption.Impact
Heap corruption from a crafted sparse file. Silent data loss from crafted dense dimensions. Any code that reads untrusted Matrix Market files is affected.
Suggested Fix
Guard every size multiplication with a checked helper (like the existing
LG_Multiply_size_t). Validatenvalsbefore allocation. Reject inputs wherenvals > INT64_MAXgiven the loop counter isint64_t.