Skip to content

Commit d7b47cd

Browse files
authored
Merge pull request #176 from github/aneubeck-triage-rust-gems-security-alerts
Fix remaining medium and low code-scanning security alerts
2 parents 0299c00 + 44639aa commit d7b47cd

6 files changed

Lines changed: 85 additions & 7 deletions

File tree

‎crates/bpe-openai/src/lib.rs‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,11 @@ impl<'a> Iterator for Splits<'a> {
209209
.next_back()
210210
.expect("Expected at least a look-ahead character!");
211211
end -= last.len_utf8();
212-
assert_ne!(end, start, "a look-ahead pattern must ALWAYS consume at least one character excluding the look-ahead character!");
213212
}
213+
assert_ne!(
214+
end, start,
215+
"a pattern must ALWAYS consume at least one character!"
216+
);
214217
self.last = end;
215218
Some(&self.text[start..end])
216219
}
@@ -277,4 +280,11 @@ mod tests {
277280
None
278281
);
279282
}
283+
284+
#[test]
285+
#[should_panic(expected = "a pattern must ALWAYS consume at least one character!")]
286+
fn test_empty_regex_match_panics() {
287+
let pre = Pretokenizer::new("a*").expect("valid regex");
288+
let _ = pre.split("b").next();
289+
}
280290
}

‎crates/consistent-choose-k/src/consistent_reservoir.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ impl ConsistentReservoir {
286286
pub fn new(k: u32, n: u32, master_key: u64) -> Self {
287287
assert!(k >= 1, "k must be at least 1");
288288
assert!(n <= 1u32 << 30, "n must be at most 2^30");
289+
assert!(k <= n, "k must be at most n");
289290
let j_max = smallest_j_max(n);
290291
let m = 1u32 << (2 * j_max + 2);
291292
let mut values = vec![];
@@ -508,4 +509,10 @@ mod tests {
508509
}
509510
}
510511
}
512+
513+
#[test]
514+
#[should_panic(expected = "k must be at most n")]
515+
fn new_panics_when_k_exceeds_n() {
516+
let _ = ConsistentReservoir::new(5, 4, 42);
517+
}
511518
}

‎crates/geo_filters/src/config.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ pub(crate) fn hash_to_bucket(phi: f64, hash: u64) -> usize {
336336
/// to most significant bits.
337337
/// The function returns the zero-based bit index of that one bit.
338338
/// If no such one bit exist, the function will return 64.
339-
#[cfg(target_arch = "x86_64")]
339+
#[cfg(all(target_arch = "x86_64", target_feature = "bmi2"))]
340340
pub(crate) fn nth_one(value: u64, n: u32) -> u32 {
341341
unsafe { std::arch::x86_64::_pdep_u64(1 << n, value).trailing_zeros() }
342342
}
@@ -345,7 +345,7 @@ pub(crate) fn nth_one(value: u64, n: u32) -> u32 {
345345
/// to most significant bits.
346346
/// The function returns the zero-based bit index of that one bit.
347347
/// If no such one bit exist, the function will return 64.
348-
#[cfg(not(target_arch = "x86_64"))]
348+
#[cfg(not(all(target_arch = "x86_64", target_feature = "bmi2")))]
349349
pub(crate) fn nth_one(mut value: u64, mut n: u32) -> u32 {
350350
while n > 0 && value != 0 {
351351
value ^= 1 << value.trailing_zeros();

‎crates/geo_filters/src/config/bitchunks.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ pub(crate) fn xor_bit_chunks(
161161

162162
/// ANDs the bit chunks with the repetitive mask without compressing the result to the one bits.
163163
/// The result will only contain non-zero BitChunks!
164+
///
165+
/// # Panics
166+
///
167+
/// Panics if `modulus` is not in `1..`[`BITS_PER_BLOCK`].
164168
pub(crate) fn mask_bit_chunks(
165169
iter: impl Iterator<Item = BitChunk>,
166170
mask: u64,
@@ -176,7 +180,16 @@ struct MaskedBitChunksIterator<I: Iterator<Item = BitChunk>> {
176180
}
177181

178182
impl<I: Iterator<Item = BitChunk>> MaskedBitChunksIterator<I> {
183+
/// Creates a new iterator that masks each [`BitChunk`] with the `modulus`-bit `mask`.
184+
///
185+
/// # Panics
186+
///
187+
/// Panics if `modulus` is not in `1..`[`BITS_PER_BLOCK`].
179188
fn new(iter: I, mask: u64, modulus: usize) -> Self {
189+
assert!(
190+
(1..BITS_PER_BLOCK).contains(&modulus),
191+
"mask_size must be in 1..{BITS_PER_BLOCK} (got {modulus})"
192+
);
180193
let mut block_mask = mask;
181194
let mut i = modulus;
182195
while i < BITS_PER_BLOCK {

‎crates/geo_filters/src/diff_count.rs‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ impl<'a, C: GeoConfig<Diff>> GeoDiffCount<'a, C> {
127127
/// To reduce the number of operations, the implementation first xors the bit chunks together,
128128
/// then applies the mask (without compressing the mask), and finally looks for the first
129129
/// modified bit. This bit must be set in the larger geometric filter.
130+
///
131+
/// # Panics
132+
///
133+
/// Panics if `mask_size` is not in `1..64`.
130134
pub fn cmp_masked(&self, other: &Self, mask: u64, mask_size: usize) -> Ordering {
131135
let mut differing_bit = mask_bit_chunks(
132136
xor_bit_chunks(self.bit_chunks(), other.bit_chunks()),
@@ -159,10 +163,15 @@ impl<'a, C: GeoConfig<Diff>> GeoDiffCount<'a, C> {
159163
/// When two keys are equal the ordering is undetermined and the caller must fall back to
160164
/// [`Self::cmp_masked`], e.g.
161165
/// `a_key.cmp(&b_key).then_with(|| a.cmp_masked(b, mask, mask_size))`.
166+
///
167+
/// # Panics
168+
///
169+
/// Panics if `mask_size` is not in `1..64` or if [`GeoConfig::bucket_position_bits`] is not in
170+
/// `1..=64`.
162171
pub fn masked_sort_key(&self, mask: u64, mask_size: usize) -> u64 {
163172
assert!(
164-
(1..u64::BITS as usize).contains(&mask_size),
165-
"mask_size must be in 1..=63 (got {mask_size})"
173+
(1..BITS_PER_BLOCK).contains(&mask_size),
174+
"mask_size must be in 1..{BITS_PER_BLOCK} (got {mask_size})"
166175
);
167176
let position_bits = self.config.bucket_position_bits();
168177
assert!(
@@ -1278,4 +1287,12 @@ mod tests {
12781287
serialization_round_trip::<GeoDiffConfig13>(rnd);
12791288
});
12801289
}
1290+
1291+
#[test]
1292+
#[should_panic(expected = "mask_size must be in 1..64")]
1293+
fn test_cmp_masked_zero_mask_size_panics() {
1294+
let a = GeoDiffCount7::default();
1295+
let b = GeoDiffCount7::default();
1296+
let _ = a.cmp_masked(&b, 0, 0);
1297+
}
12811298
}

‎crates/string-offsets/src/lib.rs‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ pub struct Pos {
151151
// line ranges for empty character ranges in the middle of a line...
152152
impl<C: ConfigType> StringOffsets<C> {
153153
/// Create a new converter to work with offsets into the given string.
154+
///
155+
/// # Panics
156+
///
157+
/// Panics if `content.len() >= u32::MAX as usize`.
154158
pub fn new(content: &str) -> Self {
155159
new_converter(content.as_bytes())
156160
}
@@ -159,6 +163,10 @@ impl<C: ConfigType> StringOffsets<C> {
159163
///
160164
/// If `content` is UTF-8, this is just like [`StringOffsets::new`]. Otherwise, the
161165
/// conversion methods will produce unspecified (but memory-safe) results.
166+
///
167+
/// # Panics
168+
///
169+
/// Panics if `content.len() >= u32::MAX as usize`.
162170
pub fn from_bytes(content: &[u8]) -> Self {
163171
new_converter(content)
164172
}
@@ -190,7 +198,7 @@ impl<C: ConfigType<HasLines = True>> StringOffsets<C> {
190198

191199
/// UTF-8 offset of the first character of a line.
192200
pub fn line_to_utf8_end(&self, line_number: usize) -> usize {
193-
self.line_to_utf8_begin(line_number + 1)
201+
self.line_to_utf8_begin(line_number.saturating_add(1))
194202
}
195203

196204
/// Return the zero-based line number of the line containing the specified UTF-8 offset.
@@ -288,7 +296,9 @@ impl<C: ConfigType<HasWhitespace = True>> StringOffsets<C> {
288296
impl<C: ConfigType<HasChars = True>> StringOffsets<C> {
289297
/// Converts a UTF-8 offset to a UTF-32 offset.
290298
pub fn utf8_to_char(&self, byte_number: usize) -> usize {
291-
self.utf8_to_char.rank(byte_number + 1) - 1
299+
self.utf8_to_char
300+
.rank(byte_number.saturating_add(1))
301+
.saturating_sub(1)
292302
}
293303

294304
/// Converts a UTF-32 offset to a UTF-8 offset.
@@ -369,8 +379,17 @@ impl<C: ConfigType<HasChars = True, HasLines = True, HasUtf16 = True>> StringOff
369379
}
370380
}
371381

382+
/// Creates a [`StringOffsets`] converter for `content`.
383+
///
384+
/// # Panics
385+
///
386+
/// Panics if `content.len() >= u32::MAX as usize`.
372387
fn new_converter<C: ConfigType>(content: &[u8]) -> StringOffsets<C> {
373388
let n = content.len();
389+
assert!(
390+
n < u32::MAX as usize,
391+
"content length + 1 must fit in u32 (got {n})"
392+
);
374393
let mut utf8_builder =
375394
BitRankBuilder::with_capacity(if C::HasChars::VALUE { n + 1 } else { 0 });
376395
let mut utf16_builder = BitRankBuilder::with_capacity(if C::HasUtf16::VALUE { n } else { 0 });
@@ -649,4 +668,16 @@ line1
649668
let lines: StringOffsets = StringOffsets::from_bytes(&content);
650669
assert_eq!(lines.utf8_to_utf16_pos(16384), pos(1, 0));
651670
}
671+
672+
#[test]
673+
fn test_saturating_usize_max_offsets() {
674+
let content = "hello\nworld";
675+
let offsets: StringOffsets = StringOffsets::new(content);
676+
assert_eq!(offsets.utf8_to_char(usize::MAX), content.chars().count());
677+
assert_eq!(
678+
offsets.utf8_to_utf16(usize::MAX),
679+
content.encode_utf16().count()
680+
);
681+
assert_eq!(offsets.line_to_utf8_end(usize::MAX), content.len());
682+
}
652683
}

0 commit comments

Comments
 (0)