From eab5b615a825bf4fffb2a606e1b60685128986a1 Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Sat, 13 Sep 2025 00:57:42 +0200 Subject: [PATCH 1/2] automata: add regression test for dense::DFA::from_bytes validation panic --- regex-automata/src/dfa/dense.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/regex-automata/src/dfa/dense.rs b/regex-automata/src/dfa/dense.rs index 057536303..f698e6813 100644 --- a/regex-automata/src/dfa/dense.rs +++ b/regex-automata/src/dfa/dense.rs @@ -5230,4 +5230,17 @@ mod tests { let got = dfa.try_search_rev(&input); assert_eq!(Err(expected), got); } + + // This panics in TransitionTable::validate if the match states are not validated first. + #[test] + fn regression_validation_order() { + let mut dfa = DFA::new("abc").unwrap(); + dfa.ms = MatchStates { + slices: vec![], + pattern_ids: vec![], + pattern_len: 1, + }; + let (buf, _) = dfa.to_bytes_native_endian(); + DFA::from_bytes(&buf).unwrap_err(); + } } From 09b55df98d058e656689a0234a2790a6d153795f Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Sat, 13 Sep 2025 00:59:15 +0200 Subject: [PATCH 2/2] automata: fix validation order in dense::DFA::from_bytes --- regex-automata/src/dfa/dense.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/regex-automata/src/dfa/dense.rs b/regex-automata/src/dfa/dense.rs index f698e6813..0dc3324ef 100644 --- a/regex-automata/src/dfa/dense.rs +++ b/regex-automata/src/dfa/dense.rs @@ -2340,10 +2340,14 @@ impl<'a> DFA<&'a [u32]> { // table, match states and accelerators below. If any validation fails, // then we return an error. let (dfa, nread) = unsafe { DFA::from_bytes_unchecked(slice)? }; + // Note: Validation order is important here: + // - MatchState::validate can be called with an untrusted DFA. + // - TransistionTable::validate uses dfa.ms through match_len + // - StartTable::validate needs a valid transition table + dfa.accels.validate()?; + dfa.ms.validate(&dfa)?; dfa.tt.validate(&dfa)?; dfa.st.validate(&dfa)?; - dfa.ms.validate(&dfa)?; - dfa.accels.validate()?; // N.B. dfa.special doesn't have a way to do unchecked deserialization, // so it has already been validated. for state in dfa.states() {