Skip to content

Commit 20b5317

Browse files
committed
automata: fix panic in dense DFA deserialization
This fixes a hole in the validation logic that accidentally permitted a dense DFA to contain a match state with zero pattern IDs. Since search code is permitted to assume that every match state has at least one corresponding pattern ID, this led to a panic. Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=63391
1 parent 5f1f1c8 commit 20b5317

File tree

2 files changed

+13
-7
lines changed

2 files changed

+13
-7
lines changed

regex-automata/src/dfa/dense.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2340,8 +2340,8 @@ impl<'a> DFA<&'a [u32]> {
23402340
// table, match states and accelerators below. If any validation fails,
23412341
// then we return an error.
23422342
let (dfa, nread) = unsafe { DFA::from_bytes_unchecked(slice)? };
2343-
dfa.tt.validate(&dfa.special)?;
2344-
dfa.st.validate(&dfa.tt)?;
2343+
dfa.tt.validate(&dfa)?;
2344+
dfa.st.validate(&dfa)?;
23452345
dfa.ms.validate(&dfa)?;
23462346
dfa.accels.validate()?;
23472347
// N.B. dfa.special doesn't have a way to do unchecked deserialization,
@@ -3593,7 +3593,8 @@ impl<T: AsRef<[u32]>> TransitionTable<T> {
35933593
///
35943594
/// That is, every state ID can be used to correctly index a state in this
35953595
/// table.
3596-
fn validate(&self, sp: &Special) -> Result<(), DeserializeError> {
3596+
fn validate(&self, dfa: &DFA<T>) -> Result<(), DeserializeError> {
3597+
let sp = &dfa.special;
35973598
for state in self.states() {
35983599
// We check that the ID itself is well formed. That is, if it's
35993600
// a special state then it must actually be a quit, dead, accel,
@@ -3611,6 +3612,13 @@ impl<T: AsRef<[u32]>> TransitionTable<T> {
36113612
wasn't actually special",
36123613
));
36133614
}
3615+
if sp.is_match_state(state.id())
3616+
&& dfa.match_len(state.id()) == 0
3617+
{
3618+
return Err(DeserializeError::generic(
3619+
"found match state with zero pattern IDs",
3620+
));
3621+
}
36143622
}
36153623
for (_, to) in state.transitions() {
36163624
if !self.is_valid(to) {
@@ -4127,10 +4135,8 @@ impl<T: AsRef<[u32]>> StartTable<T> {
41274135
/// it against the given transition table (which must be for the same DFA).
41284136
///
41294137
/// That is, every state ID can be used to correctly index a state.
4130-
fn validate(
4131-
&self,
4132-
tt: &TransitionTable<T>,
4133-
) -> Result<(), DeserializeError> {
4138+
fn validate(&self, dfa: &DFA<T>) -> Result<(), DeserializeError> {
4139+
let tt = &dfa.tt;
41344140
if !self.universal_start_unanchored.map_or(true, |s| tt.is_valid(s)) {
41354141
return Err(DeserializeError::generic(
41364142
"found invalid universal unanchored starting state ID",

0 commit comments

Comments
 (0)