Skip to content

Commit fd84861

Browse files
use checked math
1 parent 7d4a2d1 commit fd84861

1 file changed

Lines changed: 61 additions & 26 deletions

File tree

storage/src/journal/contiguous/fixed.rs

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -108,22 +108,39 @@ const RECOVERY_WATERMARK_KEY: u64 = 2;
108108

109109
/// Return the first retained logical position in `section`.
110110
#[inline]
111-
const fn first_in_section(pruning_boundary: u64, section: u64, items_per_blob: u64) -> u64 {
112-
let start = section * items_per_blob;
111+
fn first_in_section(
112+
pruning_boundary: u64,
113+
section: u64,
114+
items_per_blob: u64,
115+
) -> Result<u64, Error> {
116+
let start = section
117+
.checked_mul(items_per_blob)
118+
.ok_or(Error::OffsetOverflow)?;
113119
if pruning_boundary > start {
114-
pruning_boundary
120+
Ok(pruning_boundary)
115121
} else {
116-
start
122+
Ok(start)
117123
}
118124
}
119125

120126
/// Maximum number of items a section's blob can physically hold. This is `items_per_blob` unless
121127
/// the pruning boundary falls mid-section (from `init_at_size`), in which case the skipped prefix
122128
/// reduces the capacity.
123129
#[inline]
124-
const fn section_capacity(pruning_boundary: u64, section: u64, items_per_blob: u64) -> u64 {
125-
let start = section * items_per_blob;
126-
items_per_blob - (first_in_section(pruning_boundary, section, items_per_blob) - start)
130+
fn section_capacity(
131+
pruning_boundary: u64,
132+
section: u64,
133+
items_per_blob: u64,
134+
) -> Result<u64, Error> {
135+
let start = section
136+
.checked_mul(items_per_blob)
137+
.ok_or(Error::OffsetOverflow)?;
138+
let skipped = first_in_section(pruning_boundary, section, items_per_blob)?
139+
.checked_sub(start)
140+
.ok_or(Error::OffsetOverflow)?;
141+
items_per_blob
142+
.checked_sub(skipped)
143+
.ok_or(Error::OffsetOverflow)
127144
}
128145

129146
/// Configuration for `Journal` storage.
@@ -196,7 +213,8 @@ impl<E: Context, A: CodecFixedShared> Inner<E, A> {
196213
}
197214

198215
let section = pos / items_per_blob;
199-
let pos_in_section = pos - first_in_section(self.pruning_boundary, section, items_per_blob);
216+
let pos_in_section =
217+
pos - first_in_section(self.pruning_boundary, section, items_per_blob)?;
200218

201219
self.journal
202220
.get(section, pos_in_section)
@@ -226,7 +244,8 @@ impl<E: Context, A: CodecFixedShared> Inner<E, A> {
226244
return None;
227245
}
228246
let section = pos / items_per_blob;
229-
let pos_in_section = pos - first_in_section(self.pruning_boundary, section, items_per_blob);
247+
let pos_in_section =
248+
pos - first_in_section(self.pruning_boundary, section, items_per_blob).ok()?;
230249
self.journal.try_get_sync_into(section, pos_in_section, buf)
231250
}
232251
}
@@ -355,7 +374,7 @@ impl<E: Context, A: CodecFixedShared> super::Reader for Reader<'_, E, A> {
355374
}
356375

357376
let group_len = group_end - group_start;
358-
let first_position = first_in_section(pruning_boundary, section, items_per_blob);
377+
let first_position = first_in_section(pruning_boundary, section, items_per_blob)?;
359378
let section_positions: Vec<u64> = miss_positions[group_start..group_end]
360379
.iter()
361380
.map(|&pos| pos - first_position)
@@ -423,7 +442,7 @@ impl<E: Context, A: CodecFixedShared> super::Reader for Reader<'_, E, A> {
423442

424443
let start_section = start_pos / items_per_blob;
425444
let start_pos_in_section =
426-
start_pos - first_in_section(pruning_boundary, start_section, items_per_blob);
445+
start_pos - first_in_section(pruning_boundary, start_section, items_per_blob)?;
427446

428447
// Check all middle sections (not oldest, not tail) in range are complete.
429448
let journal = &self.guard.journal;
@@ -445,10 +464,11 @@ impl<E: Context, A: CodecFixedShared> super::Reader for Reader<'_, E, A> {
445464

446465
// Transform (section, pos_in_section, item) to (global_pos, item).
447466
let stream = inner_stream.map(move |result| {
448-
result.map(|(section, pos_in_section, item)| {
449-
let global_pos =
450-
first_in_section(pruning_boundary, section, items_per_blob) + pos_in_section;
451-
(global_pos, item)
467+
result.and_then(|(section, pos_in_section, item)| {
468+
let global_pos = first_in_section(pruning_boundary, section, items_per_blob)?
469+
.checked_add(pos_in_section)
470+
.ok_or(Error::OffsetOverflow)?;
471+
Ok((global_pos, item))
452472
})
453473
});
454474

@@ -696,7 +716,12 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
696716
meta_pruning_boundary: Option<u64>,
697717
meta_recovery_watermark: Option<u64>,
698718
) -> Result<(u64, u64, u64, Option<RecoveryRepair>), Error> {
699-
let blob_boundary = inner.oldest_section().map_or(0, |o| o * items_per_blob);
719+
let blob_boundary = match inner.oldest_section() {
720+
Some(oldest) => oldest
721+
.checked_mul(items_per_blob)
722+
.ok_or(Error::OffsetOverflow)?,
723+
None => 0,
724+
};
700725

701726
// Determine the pruning boundary from metadata and blob state.
702727
//
@@ -781,7 +806,7 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
781806
};
782807

783808
let oldest_len = inner.section_len(oldest).await?;
784-
let expected = section_capacity(pruning_boundary, oldest, items_per_blob);
809+
let expected = section_capacity(pruning_boundary, oldest, items_per_blob)?;
785810

786811
if oldest_len > expected {
787812
return Err(Error::Corruption(format!(
@@ -799,7 +824,7 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
799824
section: u64,
800825
) -> Result<(u64, u64), Error> {
801826
let len = inner.section_len(section).await?;
802-
let capacity = section_capacity(pruning_boundary, section, items_per_blob);
827+
let capacity = section_capacity(pruning_boundary, section, items_per_blob)?;
803828
if len > capacity {
804829
return Err(Error::Corruption(format!(
805830
"section {section} has too many items: expected at most {capacity}, got {len}"
@@ -838,7 +863,9 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
838863
let (tail_len, _) =
839864
Self::section_len_within_capacity(inner, items_per_blob, pruning_boundary, newest)
840865
.await?;
841-
let size = first_in_section(pruning_boundary, newest, items_per_blob) + tail_len;
866+
let size = first_in_section(pruning_boundary, newest, items_per_blob)?
867+
.checked_add(tail_len)
868+
.ok_or(Error::OffsetOverflow)?;
842869
Ok((size, None))
843870
}
844871

@@ -860,8 +887,10 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
860887

861888
// The oldest section's capacity was already checked before recovery mode dispatch.
862889
let oldest_len = inner.section_len(oldest).await?;
863-
let expected_oldest = section_capacity(pruning_boundary, oldest, items_per_blob);
864-
let mut size = pruning_boundary + oldest_len;
890+
let expected_oldest = section_capacity(pruning_boundary, oldest, items_per_blob)?;
891+
let mut size = pruning_boundary
892+
.checked_add(oldest_len)
893+
.ok_or(Error::OffsetOverflow)?;
865894

866895
if oldest == newest {
867896
return Ok((size, None));
@@ -872,7 +901,9 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
872901
size,
873902
Some(RecoveryRepair {
874903
section: oldest,
875-
byte_offset: oldest_len * Self::CHUNK_SIZE_U64,
904+
byte_offset: oldest_len
905+
.checked_mul(Self::CHUNK_SIZE_U64)
906+
.ok_or(Error::OffsetOverflow)?,
876907
}),
877908
));
878909
}
@@ -882,7 +913,7 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
882913
Self::section_len_within_capacity(inner, items_per_blob, pruning_boundary, section)
883914
.await?;
884915

885-
size += len;
916+
size = size.checked_add(len).ok_or(Error::OffsetOverflow)?;
886917
if len < capacity {
887918
if section == newest {
888919
return Ok((size, None));
@@ -891,7 +922,9 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
891922
size,
892923
Some(RecoveryRepair {
893924
section,
894-
byte_offset: len * Self::CHUNK_SIZE_U64,
925+
byte_offset: len
926+
.checked_mul(Self::CHUNK_SIZE_U64)
927+
.ok_or(Error::OffsetOverflow)?,
895928
}),
896929
));
897930
}
@@ -1164,8 +1197,10 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
11641197

11651198
let section = size / self.items_per_blob;
11661199
let pos_in_section =
1167-
size - first_in_section(inner.pruning_boundary, section, self.items_per_blob);
1168-
let byte_offset = pos_in_section * Self::CHUNK_SIZE_U64;
1200+
size - first_in_section(inner.pruning_boundary, section, self.items_per_blob)?;
1201+
let byte_offset = pos_in_section
1202+
.checked_mul(Self::CHUNK_SIZE_U64)
1203+
.ok_or(Error::OffsetOverflow)?;
11691204

11701205
let should_sync_metadata = Self::lower_recovery_watermark(&mut inner, size)?;
11711206
drop(inner);

0 commit comments

Comments
 (0)