Skip to content

Commit

Permalink
automata: fix panic in dense DFA deserialization
Browse files Browse the repository at this point in the history
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
  • Loading branch information
BurntSushi committed Oct 20, 2023
1 parent 5f1f1c8 commit 20b5317
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 7 deletions.
Binary file not shown.
20 changes: 13 additions & 7 deletions regex-automata/src/dfa/dense.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2340,8 +2340,8 @@ 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)? };
dfa.tt.validate(&dfa.special)?;
dfa.st.validate(&dfa.tt)?;
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,
Expand Down Expand Up @@ -3593,7 +3593,8 @@ impl<T: AsRef<[u32]>> TransitionTable<T> {
///
/// That is, every state ID can be used to correctly index a state in this
/// table.
fn validate(&self, sp: &Special) -> Result<(), DeserializeError> {
fn validate(&self, dfa: &DFA<T>) -> Result<(), DeserializeError> {
let sp = &dfa.special;
for state in self.states() {
// We check that the ID itself is well formed. That is, if it's
// a special state then it must actually be a quit, dead, accel,
Expand All @@ -3611,6 +3612,13 @@ impl<T: AsRef<[u32]>> TransitionTable<T> {
wasn't actually special",
));
}
if sp.is_match_state(state.id())
&& dfa.match_len(state.id()) == 0
{
return Err(DeserializeError::generic(
"found match state with zero pattern IDs",
));
}
}
for (_, to) in state.transitions() {
if !self.is_valid(to) {
Expand Down Expand Up @@ -4127,10 +4135,8 @@ impl<T: AsRef<[u32]>> StartTable<T> {
/// it against the given transition table (which must be for the same DFA).
///
/// That is, every state ID can be used to correctly index a state.
fn validate(
&self,
tt: &TransitionTable<T>,
) -> Result<(), DeserializeError> {
fn validate(&self, dfa: &DFA<T>) -> Result<(), DeserializeError> {
let tt = &dfa.tt;
if !self.universal_start_unanchored.map_or(true, |s| tt.is_valid(s)) {
return Err(DeserializeError::generic(
"found invalid universal unanchored starting state ID",
Expand Down

0 comments on commit 20b5317

Please sign in to comment.