Skip to content

Commit 3ed8fcc

Browse files
committed
fix OOB access in SIMD impl of str.contains()
1 parent d576a9b commit 3ed8fcc

File tree

1 file changed

+6
-4
lines changed

1 file changed

+6
-4
lines changed

library/core/src/str/pattern.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
17411741
use crate::simd::{SimdPartialEq, ToBitMask};
17421742

17431743
let first_probe = needle[0];
1744+
let last_byte_offset = needle.len() - 1;
17441745

17451746
// the offset used for the 2nd vector
17461747
let second_probe_offset = if needle.len() == 2 {
@@ -1758,7 +1759,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
17581759
};
17591760

17601761
// do a naive search if the haystack is too small to fit
1761-
if haystack.len() < Block::LANES + second_probe_offset {
1762+
if haystack.len() < Block::LANES + last_byte_offset {
17621763
return Some(haystack.windows(needle.len()).any(|c| c == needle));
17631764
}
17641765

@@ -1815,7 +1816,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
18151816
// The loop condition must ensure that there's enough headroom to read LANE bytes,
18161817
// and not only at the current index but also at the index shifted by block_offset
18171818
const UNROLL: usize = 4;
1818-
while i + second_probe_offset + UNROLL * Block::LANES < haystack.len() && !result {
1819+
while i + last_byte_offset + UNROLL * Block::LANES < haystack.len() && !result {
18191820
let mut masks = [0u16; UNROLL];
18201821
for j in 0..UNROLL {
18211822
masks[j] = test_chunk(i + j * Block::LANES);
@@ -1828,7 +1829,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
18281829
}
18291830
i += UNROLL * Block::LANES;
18301831
}
1831-
while i + second_probe_offset + Block::LANES < haystack.len() && !result {
1832+
while i + last_byte_offset + Block::LANES < haystack.len() && !result {
18321833
let mask = test_chunk(i);
18331834
if mask != 0 {
18341835
result |= check_mask(i, mask, result);
@@ -1840,7 +1841,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
18401841
// This simply repeats the same procedure but as right-aligned chunk instead
18411842
// of a left-aligned one. The last byte must be exactly flush with the string end so
18421843
// we don't miss a single byte or read out of bounds.
1843-
let i = haystack.len() - second_probe_offset - Block::LANES;
1844+
let i = haystack.len() - last_byte_offset - Block::LANES;
18441845
let mask = test_chunk(i);
18451846
if mask != 0 {
18461847
result |= check_mask(i, mask, result);
@@ -1860,6 +1861,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
18601861
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] // only called on x86
18611862
#[inline]
18621863
unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
1864+
debug_assert_eq!(x.len(), y.len());
18631865
// This function is adapted from
18641866
// https://github.com/BurntSushi/memchr/blob/8037d11b4357b0f07be2bb66dc2659d9cf28ad32/src/memmem/util.rs#L32
18651867

0 commit comments

Comments
 (0)