From 701436e5ead3febdbd9612eba6431221f04be093 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 1 May 2026 15:56:17 -0400 Subject: [PATCH 1/5] Prevent FixedSizeBinaryArray offset overflows --- .../src/array/fixed_size_binary_array.rs | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/arrow-array/src/array/fixed_size_binary_array.rs b/arrow-array/src/array/fixed_size_binary_array.rs index 39bfc1db4ad1..b37594093a64 100644 --- a/arrow-array/src/array/fixed_size_binary_array.rs +++ b/arrow-array/src/array/fixed_size_binary_array.rs @@ -85,16 +85,16 @@ impl FixedSizeBinaryArray { /// * `values.len() / size != nulls.len()` /// * `size == 0 && values.len() != 0` pub fn try_new( - size: i32, + value_length: i32, values: Buffer, nulls: Option, ) -> Result { - let data_type = DataType::FixedSizeBinary(size); - let s = size.to_usize().ok_or_else(|| { - ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {size}")) + let data_type = DataType::FixedSizeBinary(value_length); + let value_size = value_length.to_usize().ok_or_else(|| { + ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {value_length}")) })?; - let len = match values.len().checked_div(s) { + let len = match values.len().checked_div(value_size) { Some(len) => { if let Some(n) = nulls.as_ref() { if n.len() != len { @@ -120,15 +120,45 @@ impl FixedSizeBinaryArray { } }; + Self::validate_lengths(value_size, len)?; + Ok(Self { data_type, value_data: values, - value_length: size, + value_length, nulls, len, }) } + /// Some calculations below use i32 arithmetic which can overflow when + /// valid offsets are past i32::MAX. Until that is solved for real do not + /// permit constructing any FixedSizeBinaryArray that has a valid offset + /// past i32::MAX + fn validate_lengths(value_size: usize, len: usize) -> Result<(), ArrowError> { + if len == 0 { + return Ok(()); + } + let max_offset = value_size.checked_mul(len - 1).ok_or_else(|| { + ArrowError::InvalidArgumentError(format!( + "FixedSizeBinaryArray error: value size {value_size} * len {len} exceeds maximum valid offset" + )) + })?; + + let max_valid_offset: usize = i32::MAX.try_into().map_err(|_| { + ArrowError::InvalidArgumentError(format!( + "FixedSizeBinaryArray error: maximum valid offset exceeds i32::MAX, got {max_offset}" + )) + })?; + + if max_offset > max_valid_offset { + return Err(ArrowError::InvalidArgumentError(format!( + "FixedSizeBinaryArray error: value size {value_size} * length {len} exceeds maximum valid offset of {max_valid_offset}" + ))); + }; + Ok(()) + } + /// Create a new [`FixedSizeBinaryArray`] of length `len` where all values are null /// /// # Panics From 1152dbc28771719874f2c71844286e33dbca3f81 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 1 May 2026 16:13:54 -0400 Subject: [PATCH 2/5] Check other paths, add errors --- .../src/array/fixed_size_binary_array.rs | 99 ++++++++++++++----- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/arrow-array/src/array/fixed_size_binary_array.rs b/arrow-array/src/array/fixed_size_binary_array.rs index b37594093a64..f05760147d51 100644 --- a/arrow-array/src/array/fixed_size_binary_array.rs +++ b/arrow-array/src/array/fixed_size_binary_array.rs @@ -64,8 +64,8 @@ impl FixedSizeBinaryArray { /// # Panics /// /// Panics if [`Self::try_new`] returns an error - pub fn new(size: i32, values: Buffer, nulls: Option) -> Self { - Self::try_new(size, values, nulls).unwrap() + pub fn new(value_length: i32, values: Buffer, nulls: Option) -> Self { + Self::try_new(value_length, values, nulls).unwrap() } /// Create a new [`Scalar`] from `value` @@ -165,16 +165,18 @@ impl FixedSizeBinaryArray { /// /// Panics if /// - /// * `size < 0` - /// * `size * len` would overflow `usize` - pub fn new_null(size: i32, len: usize) -> Self { + /// * `value_length < 0` + /// * `value_length * len` would overflow `usize` + pub fn new_null(value_length: i32, len: usize) -> Self { const BITS_IN_A_BYTE: usize = 8; - let capacity_in_bytes = size.to_usize().unwrap().checked_mul(len).unwrap(); + let value_size = value_length.to_usize().unwrap(); + Self::validate_lengths(value_size, len).unwrap(); + let capacity_in_bytes = value_size.checked_mul(len).unwrap(); Self { - data_type: DataType::FixedSizeBinary(size), + data_type: DataType::FixedSizeBinary(value_length), value_data: MutableBuffer::new_null(capacity_in_bytes * BITS_IN_A_BYTE).into(), nulls: Some(NullBuffer::new_null(len)), - value_length: size, + value_length, len, } } @@ -364,12 +366,18 @@ impl FixedSizeBinaryArray { let nulls = NullBuffer::from_unsliced_buffer(null_buf, len); - let size = size.unwrap_or(0) as i32; + let value_size = size.unwrap_or(0); + Self::validate_lengths(value_size, len)?; + let value_length = value_size.try_into().map_err(|_| { + ArrowError::InvalidArgumentError(format!( + "FixedSizeBinaryArray value length exceeds i32, got {value_size}" + )) + })?; Ok(Self { - data_type: DataType::FixedSizeBinary(size), + data_type: DataType::FixedSizeBinary(value_length), value_data: buffer.into(), nulls, - value_length: size, + value_length, len, }) } @@ -398,17 +406,28 @@ impl FixedSizeBinaryArray { /// # Errors /// /// Returns error if argument has length zero, or sizes of nested slices don't match. - pub fn try_from_sparse_iter_with_size(mut iter: T, size: i32) -> Result + pub fn try_from_sparse_iter_with_size( + mut iter: T, + value_length: i32, + ) -> Result where T: Iterator>, U: AsRef<[u8]>, { + let value_size = value_length.to_usize().ok_or_else(|| { + ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {value_length}")) + })?; let mut len = 0; let mut byte = 0; let iter_size_hint = iter.size_hint().0; let mut null_buf = MutableBuffer::new(bit_util::ceil(iter_size_hint, 8)); - let mut buffer = MutableBuffer::new(iter_size_hint * (size as usize)); + let capacity = iter_size_hint.checked_mul(value_size).ok_or_else(|| { + ArrowError::InvalidArgumentError(format!( + "FixedSizeBinaryArray error: value size {value_size} * len hint {iter_size_hint} exceeds usize" + )) + })?; + let mut buffer = MutableBuffer::new(capacity); iter.try_for_each(|item| -> Result<(), ArrowError> { // extend null bitmask by one byte per each 8 items @@ -420,10 +439,10 @@ impl FixedSizeBinaryArray { if let Some(slice) = item { let slice = slice.as_ref(); - if size as usize != slice.len() { + if value_size != slice.len() { return Err(ArrowError::InvalidArgumentError(format!( "Nested array size mismatch: one is {}, and the other is {}", - size, + value_length, slice.len() ))); } @@ -431,7 +450,7 @@ impl FixedSizeBinaryArray { bit_util::set_bit(null_buf.as_slice_mut(), len); buffer.extend_from_slice(slice); } else { - buffer.extend_zeros(size as usize); + buffer.extend_zeros(value_size); } len += 1; @@ -440,13 +459,14 @@ impl FixedSizeBinaryArray { })?; let nulls = NullBuffer::from_unsliced_buffer(null_buf, len); + Self::validate_lengths(value_size, len)?; Ok(Self { - data_type: DataType::FixedSizeBinary(size), + data_type: DataType::FixedSizeBinary(value_length), value_data: buffer.into(), nulls, len, - value_length: size, + value_length, }) } @@ -506,12 +526,18 @@ impl FixedSizeBinaryArray { )); } - let size = size.unwrap_or(0).try_into().unwrap(); + let value_size = size.unwrap_or(0); + Self::validate_lengths(value_size, len)?; + let value_length = value_size.try_into().map_err(|_| { + ArrowError::InvalidArgumentError(format!( + "FixedSizeBinaryArray value length exceeds i32, got {value_size}" + )) + })?; Ok(Self { - data_type: DataType::FixedSizeBinary(size), + data_type: DataType::FixedSizeBinary(value_length), value_data: buffer.into(), nulls: None, - value_length: size, + value_length, len, }) } @@ -541,8 +567,15 @@ impl From for FixedSizeBinaryArray { _ => panic!("Expected data type to be FixedSizeBinary"), }; - let size = value_length as usize; - let value_data = buffers[0].slice_with_length(offset * size, len * size); + let value_size = value_length + .to_usize() + .expect("FixedSizeBinaryArray value length must be non-negative"); + Self::validate_lengths(value_size, len) + .expect("FixedSizeBinaryArray offsets must fit within i32"); + let value_data = buffers[0].slice_with_length( + offset.checked_mul(value_size).expect("offset overflow"), + len.checked_mul(value_size).expect("length overflow"), + ); Self { data_type, @@ -1033,6 +1066,26 @@ mod tests { array.value(4); } + #[test] + fn test_validate_lengths_allows_empty_array() { + FixedSizeBinaryArray::validate_lengths(1024, 0).unwrap(); + } + + #[test] + fn test_validate_lengths_allows_i32_max_offset() { + FixedSizeBinaryArray::validate_lengths(1, (i32::MAX as usize) + 1).unwrap(); + FixedSizeBinaryArray::validate_lengths(262_176, 8192).unwrap(); + } + + #[test] + fn test_validate_lengths_rejects_offset_past_i32_max() { + let err = FixedSizeBinaryArray::validate_lengths(262_177, 8192).unwrap_err(); + assert_eq!( + err.to_string(), + "Invalid argument error: FixedSizeBinaryArray error: value size 262177 * length 8192 exceeds maximum valid offset of 2147483647", + ); + } + #[test] fn test_constructors() { let buffer = Buffer::from_vec(vec![0_u8; 10]); From b4ea0e87e238cad84e7e6ef813501828547aac53 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 1 May 2026 16:34:49 -0400 Subject: [PATCH 3/5] Adjust bounds --- .../src/array/fixed_size_binary_array.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/arrow-array/src/array/fixed_size_binary_array.rs b/arrow-array/src/array/fixed_size_binary_array.rs index f05760147d51..d20ff9d7faa9 100644 --- a/arrow-array/src/array/fixed_size_binary_array.rs +++ b/arrow-array/src/array/fixed_size_binary_array.rs @@ -71,7 +71,9 @@ impl FixedSizeBinaryArray { /// Create a new [`Scalar`] from `value` pub fn new_scalar(value: impl AsRef<[u8]>) -> Scalar { let v = value.as_ref(); - Scalar::new(Self::new(v.len() as _, Buffer::from(v), None)) + let value_length = + i32::try_from(v.len()).expect("FixedSizeBinaryArray value length exceeds i32"); + Scalar::new(Self::new(value_length, Buffer::from(v), None)) } /// Create a new [`FixedSizeBinaryArray`] from the provided parts, returning an error on failure @@ -84,6 +86,7 @@ impl FixedSizeBinaryArray { /// * `size < 0` /// * `values.len() / size != nulls.len()` /// * `size == 0 && values.len() != 0` + /// * `len * size > i32::MAX` pub fn try_new( value_length: i32, values: Buffer, @@ -136,10 +139,9 @@ impl FixedSizeBinaryArray { /// permit constructing any FixedSizeBinaryArray that has a valid offset /// past i32::MAX fn validate_lengths(value_size: usize, len: usize) -> Result<(), ArrowError> { - if len == 0 { - return Ok(()); - } - let max_offset = value_size.checked_mul(len - 1).ok_or_else(|| { + // the offset is also calculated for the next element (i + 1) so + // check `len` (not last element index) to ensure that all offsets are valid + let max_offset = value_size.checked_mul(len).ok_or_else(|| { ArrowError::InvalidArgumentError(format!( "FixedSizeBinaryArray error: value size {value_size} * len {len} exceeds maximum valid offset" )) @@ -167,6 +169,7 @@ impl FixedSizeBinaryArray { /// /// * `value_length < 0` /// * `value_length * len` would overflow `usize` + /// * `value_length * len > i32::MAX` pub fn new_null(value_length: i32, len: usize) -> Self { const BITS_IN_A_BYTE: usize = 8; let value_size = value_length.to_usize().unwrap(); @@ -1073,8 +1076,8 @@ mod tests { #[test] fn test_validate_lengths_allows_i32_max_offset() { - FixedSizeBinaryArray::validate_lengths(1, (i32::MAX as usize) + 1).unwrap(); - FixedSizeBinaryArray::validate_lengths(262_176, 8192).unwrap(); + FixedSizeBinaryArray::validate_lengths(1, i32::MAX as usize).unwrap(); + FixedSizeBinaryArray::validate_lengths(262_176, 8191).unwrap(); } #[test] From e47512a35fd1479b937384a9c7eb09ec99f89236 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 1 May 2026 16:48:50 -0400 Subject: [PATCH 4/5] A few more potential overflows --- .../src/array/fixed_size_binary_array.rs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/arrow-array/src/array/fixed_size_binary_array.rs b/arrow-array/src/array/fixed_size_binary_array.rs index d20ff9d7faa9..2576c67be5b6 100644 --- a/arrow-array/src/array/fixed_size_binary_array.rs +++ b/arrow-array/src/array/fixed_size_binary_array.rs @@ -170,14 +170,16 @@ impl FixedSizeBinaryArray { /// * `value_length < 0` /// * `value_length * len` would overflow `usize` /// * `value_length * len > i32::MAX` + /// * `value_length * len * 8` would overflow `usize` pub fn new_null(value_length: i32, len: usize) -> Self { const BITS_IN_A_BYTE: usize = 8; let value_size = value_length.to_usize().unwrap(); Self::validate_lengths(value_size, len).unwrap(); let capacity_in_bytes = value_size.checked_mul(len).unwrap(); + let capacity_in_bits = capacity_in_bytes.checked_mul(BITS_IN_A_BYTE).unwrap(); Self { data_type: DataType::FixedSizeBinary(value_length), - value_data: MutableBuffer::new_null(capacity_in_bytes * BITS_IN_A_BYTE).into(), + value_data: MutableBuffer::new_null(capacity_in_bits).into(), nulls: Some(NullBuffer::new_null(len)), value_length, len, @@ -345,8 +347,16 @@ impl FixedSizeBinaryArray { // Now that we know how large each element is we can reserve // sufficient capacity in the underlying mutable buffer for // the data. - buffer.reserve(iter_size_hint * len); - buffer.extend_zeros(slice.len() * prepend); + if let Some(capacity) = iter_size_hint.checked_mul(len) { + buffer.reserve(capacity); + } + let prepend_zeros = slice.len().checked_mul(prepend).ok_or_else(|| { + ArrowError::InvalidArgumentError(format!( + "FixedSizeBinaryArray error: value size {} * prepend {prepend} exceeds usize", + slice.len() + )) + })?; + buffer.extend_zeros(prepend_zeros); } bit_util::set_bit(null_buf.as_slice_mut(), len); buffer.extend_from_slice(slice); @@ -513,7 +523,9 @@ impl FixedSizeBinaryArray { } else { let len = slice.len(); size = Some(len); - buffer.reserve(iter_size_hint * len); + if let Some(capacity) = iter_size_hint.checked_mul(len) { + buffer.reserve(capacity); + } } buffer.extend_from_slice(slice); From 4d0c5e01dcf1fef79ef1f4c8495a000ac46d5090 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Fri, 1 May 2026 16:56:18 -0400 Subject: [PATCH 5/5] [arrow-array] revert naming-only size changes --- .../src/array/fixed_size_binary_array.rs | 100 +++++++++--------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/arrow-array/src/array/fixed_size_binary_array.rs b/arrow-array/src/array/fixed_size_binary_array.rs index 2576c67be5b6..78059bebbe91 100644 --- a/arrow-array/src/array/fixed_size_binary_array.rs +++ b/arrow-array/src/array/fixed_size_binary_array.rs @@ -64,16 +64,15 @@ impl FixedSizeBinaryArray { /// # Panics /// /// Panics if [`Self::try_new`] returns an error - pub fn new(value_length: i32, values: Buffer, nulls: Option) -> Self { - Self::try_new(value_length, values, nulls).unwrap() + pub fn new(size: i32, values: Buffer, nulls: Option) -> Self { + Self::try_new(size, values, nulls).unwrap() } /// Create a new [`Scalar`] from `value` pub fn new_scalar(value: impl AsRef<[u8]>) -> Scalar { let v = value.as_ref(); - let value_length = - i32::try_from(v.len()).expect("FixedSizeBinaryArray value length exceeds i32"); - Scalar::new(Self::new(value_length, Buffer::from(v), None)) + let size = i32::try_from(v.len()).expect("FixedSizeBinaryArray value length exceeds i32"); + Scalar::new(Self::new(size, Buffer::from(v), None)) } /// Create a new [`FixedSizeBinaryArray`] from the provided parts, returning an error on failure @@ -88,16 +87,16 @@ impl FixedSizeBinaryArray { /// * `size == 0 && values.len() != 0` /// * `len * size > i32::MAX` pub fn try_new( - value_length: i32, + size: i32, values: Buffer, nulls: Option, ) -> Result { - let data_type = DataType::FixedSizeBinary(value_length); - let value_size = value_length.to_usize().ok_or_else(|| { - ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {value_length}")) + let data_type = DataType::FixedSizeBinary(size); + let s = size.to_usize().ok_or_else(|| { + ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {size}")) })?; - let len = match values.len().checked_div(value_size) { + let len = match values.len().checked_div(s) { Some(len) => { if let Some(n) = nulls.as_ref() { if n.len() != len { @@ -123,12 +122,12 @@ impl FixedSizeBinaryArray { } }; - Self::validate_lengths(value_size, len)?; + Self::validate_lengths(s, len)?; Ok(Self { data_type, value_data: values, - value_length, + value_length: size, nulls, len, }) @@ -167,21 +166,21 @@ impl FixedSizeBinaryArray { /// /// Panics if /// - /// * `value_length < 0` - /// * `value_length * len` would overflow `usize` - /// * `value_length * len > i32::MAX` - /// * `value_length * len * 8` would overflow `usize` - pub fn new_null(value_length: i32, len: usize) -> Self { + /// * `size < 0` + /// * `size * len` would overflow `usize` + /// * `size * len > i32::MAX` + /// * `size * len * 8` would overflow `usize` + pub fn new_null(size: i32, len: usize) -> Self { const BITS_IN_A_BYTE: usize = 8; - let value_size = value_length.to_usize().unwrap(); - Self::validate_lengths(value_size, len).unwrap(); - let capacity_in_bytes = value_size.checked_mul(len).unwrap(); + let size_usize = size.to_usize().unwrap(); + Self::validate_lengths(size_usize, len).unwrap(); + let capacity_in_bytes = size_usize.checked_mul(len).unwrap(); let capacity_in_bits = capacity_in_bytes.checked_mul(BITS_IN_A_BYTE).unwrap(); Self { - data_type: DataType::FixedSizeBinary(value_length), + data_type: DataType::FixedSizeBinary(size), value_data: MutableBuffer::new_null(capacity_in_bits).into(), nulls: Some(NullBuffer::new_null(len)), - value_length, + value_length: size, len, } } @@ -379,18 +378,18 @@ impl FixedSizeBinaryArray { let nulls = NullBuffer::from_unsliced_buffer(null_buf, len); - let value_size = size.unwrap_or(0); - Self::validate_lengths(value_size, len)?; - let value_length = value_size.try_into().map_err(|_| { + let size = size.unwrap_or(0); + Self::validate_lengths(size, len)?; + let size = size.try_into().map_err(|_| { ArrowError::InvalidArgumentError(format!( - "FixedSizeBinaryArray value length exceeds i32, got {value_size}" + "FixedSizeBinaryArray value length exceeds i32, got {size}" )) })?; Ok(Self { - data_type: DataType::FixedSizeBinary(value_length), + data_type: DataType::FixedSizeBinary(size), value_data: buffer.into(), nulls, - value_length, + value_length: size, len, }) } @@ -419,25 +418,22 @@ impl FixedSizeBinaryArray { /// # Errors /// /// Returns error if argument has length zero, or sizes of nested slices don't match. - pub fn try_from_sparse_iter_with_size( - mut iter: T, - value_length: i32, - ) -> Result + pub fn try_from_sparse_iter_with_size(mut iter: T, size: i32) -> Result where T: Iterator>, U: AsRef<[u8]>, { - let value_size = value_length.to_usize().ok_or_else(|| { - ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {value_length}")) + let size_usize = size.to_usize().ok_or_else(|| { + ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {size}")) })?; let mut len = 0; let mut byte = 0; let iter_size_hint = iter.size_hint().0; let mut null_buf = MutableBuffer::new(bit_util::ceil(iter_size_hint, 8)); - let capacity = iter_size_hint.checked_mul(value_size).ok_or_else(|| { + let capacity = iter_size_hint.checked_mul(size_usize).ok_or_else(|| { ArrowError::InvalidArgumentError(format!( - "FixedSizeBinaryArray error: value size {value_size} * len hint {iter_size_hint} exceeds usize" + "FixedSizeBinaryArray error: value size {size_usize} * len hint {iter_size_hint} exceeds usize" )) })?; let mut buffer = MutableBuffer::new(capacity); @@ -452,10 +448,10 @@ impl FixedSizeBinaryArray { if let Some(slice) = item { let slice = slice.as_ref(); - if value_size != slice.len() { + if size_usize != slice.len() { return Err(ArrowError::InvalidArgumentError(format!( "Nested array size mismatch: one is {}, and the other is {}", - value_length, + size, slice.len() ))); } @@ -463,7 +459,7 @@ impl FixedSizeBinaryArray { bit_util::set_bit(null_buf.as_slice_mut(), len); buffer.extend_from_slice(slice); } else { - buffer.extend_zeros(value_size); + buffer.extend_zeros(size_usize); } len += 1; @@ -472,14 +468,14 @@ impl FixedSizeBinaryArray { })?; let nulls = NullBuffer::from_unsliced_buffer(null_buf, len); - Self::validate_lengths(value_size, len)?; + Self::validate_lengths(size_usize, len)?; Ok(Self { - data_type: DataType::FixedSizeBinary(value_length), + data_type: DataType::FixedSizeBinary(size), value_data: buffer.into(), nulls, len, - value_length, + value_length: size, }) } @@ -541,18 +537,18 @@ impl FixedSizeBinaryArray { )); } - let value_size = size.unwrap_or(0); - Self::validate_lengths(value_size, len)?; - let value_length = value_size.try_into().map_err(|_| { + let size = size.unwrap_or(0); + Self::validate_lengths(size, len)?; + let size = size.try_into().map_err(|_| { ArrowError::InvalidArgumentError(format!( - "FixedSizeBinaryArray value length exceeds i32, got {value_size}" + "FixedSizeBinaryArray value length exceeds i32, got {size}" )) })?; Ok(Self { - data_type: DataType::FixedSizeBinary(value_length), + data_type: DataType::FixedSizeBinary(size), value_data: buffer.into(), nulls: None, - value_length, + value_length: size, len, }) } @@ -582,14 +578,14 @@ impl From for FixedSizeBinaryArray { _ => panic!("Expected data type to be FixedSizeBinary"), }; - let value_size = value_length + let size = value_length .to_usize() .expect("FixedSizeBinaryArray value length must be non-negative"); - Self::validate_lengths(value_size, len) + Self::validate_lengths(size, len) .expect("FixedSizeBinaryArray offsets must fit within i32"); let value_data = buffers[0].slice_with_length( - offset.checked_mul(value_size).expect("offset overflow"), - len.checked_mul(value_size).expect("length overflow"), + offset.checked_mul(size).expect("offset overflow"), + len.checked_mul(size).expect("length overflow"), ); Self {