Skip to content

Commit

Permalink
bug: fix CaptureLocations::get to handle invalid offsets
Browse files Browse the repository at this point in the history
The contract of this function says that any invalid group offset should
result in a return value of None. In general, it worked fine, unless the
offset was so big that some internal multiplication overflowed. That
could in turn produce an incorrect result or a panic. So we fix that
here with checked arithmetic.

Fixes #738, Fixes #950
  • Loading branch information
BurntSushi committed Mar 6, 2023
1 parent b85f15f commit 4cdc37e
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/re_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,27 @@ impl<'r> FusedIterator for CaptureNames<'r> {}
/// In order to build a value of this type, you'll need to call the
/// `capture_locations` method on the `Regex` being used to execute the search.
/// The value returned can then be reused in subsequent searches.
///
/// # Example
///
/// This example shows how to create and use `CaptureLocations` in a search.
///
/// ```
/// use regex::bytes::Regex;
///
/// let re = Regex::new(r"(?<first>\w+)\s+(?<last>\w+)").unwrap();
/// let mut locs = re.capture_locations();
/// let m = re.captures_read(&mut locs, b"Bruce Springsteen").unwrap();
/// assert_eq!(0..17, m.range());
/// assert_eq!(Some((0, 17)), locs.get(0));
/// assert_eq!(Some((0, 5)), locs.get(1));
/// assert_eq!(Some((6, 17)), locs.get(2));
///
/// // Asking for an invalid capture group always returns None.
/// assert_eq!(None, locs.get(3));
/// assert_eq!(None, locs.get(34973498648));
/// assert_eq!(None, locs.get(9944060567225171988));
/// ```
#[derive(Clone, Debug)]
pub struct CaptureLocations(re_trait::Locations);

Expand Down
2 changes: 1 addition & 1 deletion src/re_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl Locations {
/// not match anything. The positions returned are *always* byte indices
/// with respect to the original string matched.
pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
let (s, e) = (i * 2, i * 2 + 1);
let (s, e) = (i.checked_mul(2)?, i.checked_mul(2)?.checked_add(1)?);
match (self.0.get(s), self.0.get(e)) {
(Some(&Some(s)), Some(&Some(e))) => Some((s, e)),
_ => None,
Expand Down
21 changes: 21 additions & 0 deletions src/re_unicode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,27 @@ impl<'r, 't> FusedIterator for SplitN<'r, 't> {}
/// In order to build a value of this type, you'll need to call the
/// `capture_locations` method on the `Regex` being used to execute the search.
/// The value returned can then be reused in subsequent searches.
///
/// # Example
///
/// This example shows how to create and use `CaptureLocations` in a search.
///
/// ```
/// use regex::Regex;
///
/// let re = Regex::new(r"(?<first>\w+)\s+(?<last>\w+)").unwrap();
/// let mut locs = re.capture_locations();
/// let m = re.captures_read(&mut locs, "Bruce Springsteen").unwrap();
/// assert_eq!(0..17, m.range());
/// assert_eq!(Some((0, 17)), locs.get(0));
/// assert_eq!(Some((0, 5)), locs.get(1));
/// assert_eq!(Some((6, 17)), locs.get(2));
///
/// // Asking for an invalid capture group always returns None.
/// assert_eq!(None, locs.get(3));
/// assert_eq!(None, locs.get(34973498648));
/// assert_eq!(None, locs.get(9944060567225171988));
/// ```
#[derive(Clone, Debug)]
pub struct CaptureLocations(re_trait::Locations);

Expand Down

0 comments on commit 4cdc37e

Please sign in to comment.