From 0289c9f936872252336f213d1889b51e49f5f849 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Sat, 25 Apr 2026 11:20:29 -0400 Subject: [PATCH] Prevent Rows row index overflow (#9817) - None. Rows used unchecked usize arithmetic when validating a requested row index. In optimized builds, very large indexes could wrap the bounds check before reaching the unchecked row access path. This adds checked arithmetic for row index validation and reuses it for both Rows::row and Rows::row_len. Yes. This adds regression coverage for overflowing row indexes. Invalid row indexes that overflow during bounds validation now panic consistently. There are no API changes. --- arrow-row/src/lib.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/arrow-row/src/lib.rs b/arrow-row/src/lib.rs index 4cafbc2748ee..01de7f78250e 100644 --- a/arrow-row/src/lib.rs +++ b/arrow-row/src/lib.rs @@ -1132,14 +1132,20 @@ impl Rows { /// Returns the row at index `row` pub fn row(&self, row: usize) -> Row<'_> { - assert!(row + 1 < self.offsets.len()); + self.checked_row_end(row); unsafe { self.row_unchecked(row) } } + fn checked_row_end(&self, row: usize) -> usize { + row.checked_add(1) + .filter(|end| *end < self.offsets.len()) + .expect("row index out of bounds") + } + /// Returns the row at `index` without bounds checking /// /// # Safety - /// Caller must ensure that `index` is less than the number of offsets (#rows + 1) + /// Caller must ensure that `index + 1` is less than the number of offsets (#rows + 1) pub unsafe fn row_unchecked(&self, index: usize) -> Row<'_> { let end = unsafe { self.offsets.get_unchecked(index + 1) }; let start = unsafe { self.offsets.get_unchecked(index) }; @@ -4283,4 +4289,13 @@ mod tests { "{empty_rows_size_with_preallocate_data} should be larger than {empty_rows_size_without_preallocate}" ); } + + #[test] + #[should_panic(expected = "row index out of bounds")] + fn row_should_panic_on_overflowing_index() { + let rows = RowConverter::new(vec![SortField::new(DataType::Int32)]) + .unwrap() + .empty_rows(0, 0); + rows.row(usize::MAX); + } }