mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
expr: avoid panic on regex retry-limit
Treat onig retry-limit-in-match errors as no match. GNU expr returns exit 1 for the same input. Fixes #11612
This commit is contained in:
committed by
Daniel Hofstetter
parent
a3ac3badb8
commit
2d5e77deb2
@@ -9,7 +9,7 @@ use std::{cell::Cell, collections::BTreeMap};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
use onig::{Regex, RegexOptions, Syntax};
|
||||
use onig::{MatchParam, Regex, RegexOptions, SearchOptions, Syntax};
|
||||
|
||||
use crate::{
|
||||
ExprError, ExprResult,
|
||||
@@ -366,6 +366,25 @@ fn build_regex(pattern_bytes: Vec<u8>) -> ExprResult<(Regex, String)> {
|
||||
Ok((re, re_string))
|
||||
}
|
||||
|
||||
/// Run a regex search, treating runtime match errors as no match.
|
||||
fn regex_search<T: onig::EncodedChars>(
|
||||
regex: &Regex,
|
||||
chars: T,
|
||||
to: usize,
|
||||
region: &mut onig::Region,
|
||||
) -> Option<usize> {
|
||||
regex
|
||||
.search_with_param(
|
||||
chars,
|
||||
0,
|
||||
to,
|
||||
SearchOptions::SEARCH_OPTION_NONE,
|
||||
Some(region),
|
||||
MatchParam::default(),
|
||||
)
|
||||
.unwrap_or(None)
|
||||
}
|
||||
|
||||
/// Find matches in the input using the compiled regex
|
||||
fn find_match(regex: Regex, re_string: String, left_bytes: Vec<u8>) -> String {
|
||||
use onig::EncodedBytes;
|
||||
@@ -380,13 +399,7 @@ fn find_match(regex: Regex, re_string: String, left_bytes: Vec<u8>) -> String {
|
||||
// In UTF-8 locale, check if input is valid UTF-8
|
||||
if let Ok(left_str) = std::str::from_utf8(&left_bytes) {
|
||||
// Valid UTF-8, match as UTF-8
|
||||
let pos = regex.search_with_encoding(
|
||||
left_str,
|
||||
0,
|
||||
left_str.len(),
|
||||
onig::SearchOptions::SEARCH_OPTION_NONE,
|
||||
Some(&mut region),
|
||||
);
|
||||
let pos = regex_search(®ex, left_str, left_str.len(), &mut region);
|
||||
|
||||
if pos.is_some() {
|
||||
if regex.captures_len() > 0 {
|
||||
@@ -421,13 +434,7 @@ fn find_match(regex: Regex, re_string: String, left_bytes: Vec<u8>) -> String {
|
||||
.ok();
|
||||
|
||||
if let Some(re_ascii) = re_ascii {
|
||||
let pos = re_ascii.search_with_encoding(
|
||||
left_encoded,
|
||||
0,
|
||||
left_bytes.len(),
|
||||
onig::SearchOptions::SEARCH_OPTION_NONE,
|
||||
Some(&mut region),
|
||||
);
|
||||
let pos = regex_search(&re_ascii, left_encoded, left_bytes.len(), &mut region);
|
||||
|
||||
if pos.is_some() {
|
||||
if re_ascii.captures_len() > 0 {
|
||||
@@ -469,13 +476,7 @@ fn find_match(regex: Regex, re_string: String, left_bytes: Vec<u8>) -> String {
|
||||
UEncoding::Ascii => {
|
||||
// In ASCII/C locale, work with bytes directly
|
||||
let left_encoded = EncodedBytes::ascii(&left_bytes);
|
||||
let pos = regex.search_with_encoding(
|
||||
left_encoded,
|
||||
0,
|
||||
left_bytes.len(),
|
||||
onig::SearchOptions::SEARCH_OPTION_NONE,
|
||||
Some(&mut region),
|
||||
);
|
||||
let pos = regex_search(®ex, left_encoded, left_bytes.len(), &mut region);
|
||||
|
||||
if pos.is_some() {
|
||||
if regex.captures_len() > 0 {
|
||||
@@ -515,13 +516,7 @@ fn evaluate_match_expression(left_bytes: Vec<u8>, right_bytes: Vec<u8>) -> ExprR
|
||||
// Try to find the actual capture bytes for ASCII locale
|
||||
let mut region = onig::Region::new();
|
||||
let left_encoded = onig::EncodedBytes::ascii(&left_bytes);
|
||||
let pos = regex.search_with_encoding(
|
||||
left_encoded,
|
||||
0,
|
||||
left_bytes.len(),
|
||||
onig::SearchOptions::SEARCH_OPTION_NONE,
|
||||
Some(&mut region),
|
||||
);
|
||||
let pos = regex_search(®ex, left_encoded, left_bytes.len(), &mut region);
|
||||
|
||||
if pos.is_some() {
|
||||
if let Some((start, end)) = region.pos(1) {
|
||||
|
||||
@@ -488,6 +488,15 @@ fn test_regex_newline() {
|
||||
.stdout_only("18\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex_catastrophic_backtracking() {
|
||||
let input = "a".repeat(30) + "c";
|
||||
new_ucmd!()
|
||||
.args(&[input.as_str(), ":", "\\(a\\+a\\+\\)\\+b"])
|
||||
.fails_with_code(1)
|
||||
.stdout_only("\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_substr() {
|
||||
new_ucmd!()
|
||||
|
||||
Reference in New Issue
Block a user