Skip to content

parser: +fix Malformed .mata input only validated by asserts, crashes Release builds - #877

Draft
Adda0 wants to merge 1 commit into
develfrom
issue-761-parser-input-crashes-pr
Draft

Adda0 wants to merge 1 commit into
develfrom
issue-761-parser-input-crashes-pr

Conversation

@Adda0

@Adda0 Adda0 commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

The .mata parser validated input structure only with assert(), which vanishes in
Release builds (-DNDEBUG). Malformed input caused out-of-bounds reads (segfaults)
or was silently misparsed, instead of raising an error naming the offending
construct.

This commit replaces input-validating asserts with real checks that throw
std::runtime_error, while keeping asserts only for genuine internal invariants
(not input shape).

Fixed validations:

  • get_naming_type(): throw on unknown suffix (auto, enum, marked, chars, utf)
  • get_alphabet_type(): throw on unknown suffix (bits, explicit, intervals)
  • mf_to_aut(): throw on unknown automaton type (NFA, AFA, NFT)
  • parse_transition(): throw when transition tokens < 2, or < 3 for explicit NFA/NFT
  • ParsedSection::operator[]: throw when key is missing instead of returning garbage
  • builder::construct(IntermediateAut, Alphabet*): throw on null alphabet

Bare @NFA and %Alphabet without suffix continue to be accepted for Release
compatibility; Debug now accepts them too via explicit empty-suffix checks.

Python bindings: added except + to parse_from_mf, construct, and mintermize
in parser.pxd so C++ exceptions surface as Python exceptions rather than crashes.

BEFORE (Release build):

  • malformed transition "q0": SIGSEGV
  • malformed transition "q0 1": SIGSEGV (out-of-bounds rhs[1])
  • malformed transition "q0 1 q1 q2": silently dropped q2, accepted
  • bad alphabet type "bitz": accepted as explicit
  • bad naming "States-numbers": misinterpreted as "States-auto"
  • sec["Final"] on missing key: returned garbage size

AFTER:

  • malformed transition cases: throw std::runtime_error
  • bad type suffixes: throw std::runtime_error
  • ParsedSection["missing"]: throw std::runtime_error

Fixes #761.

Fixes #761

… Release builds

The .mata parser validated input structure only with assert(), which vanishes in
Release builds (-DNDEBUG). Malformed input caused out-of-bounds reads (segfaults)
or was silently misparsed, instead of raising an error naming the offending
construct.

This commit replaces input-validating asserts with real checks that throw
std::runtime_error, while keeping asserts only for genuine internal invariants
(not input shape).

Fixed validations:
- get_naming_type(): throw on unknown suffix (auto, enum, marked, chars, utf)
- get_alphabet_type(): throw on unknown suffix (bits, explicit, intervals)
- mf_to_aut(): throw on unknown automaton type (NFA, AFA, NFT)
- parse_transition(): throw when transition tokens < 2, or < 3 for explicit NFA/NFT
- ParsedSection::operator[]: throw when key is missing instead of returning garbage
- builder::construct(IntermediateAut, Alphabet*): throw on null alphabet

Bare @NFA and %Alphabet without suffix continue to be accepted for Release
compatibility; Debug now accepts them too via explicit empty-suffix checks.

Python bindings: added `except +` to parse_from_mf, construct, and mintermize
in parser.pxd so C++ exceptions surface as Python exceptions rather than crashes.

BEFORE (Release build):
- malformed transition "q0": SIGSEGV
- malformed transition "q0 1": SIGSEGV (out-of-bounds rhs[1])
- malformed transition "q0 1 q1 q2": silently dropped q2, accepted
- bad alphabet type "bitz": accepted as explicit
- bad naming "States-numbers": misinterpreted as "States-auto"
- sec["Final"] on missing key: returned garbage size

AFTER:
- malformed transition cases: throw std::runtime_error
- bad type suffixes: throw std::runtime_error
- ParsedSection["missing"]: throw std::runtime_error

Fixes #761.
Adda0 added a commit that referenced this pull request Oct 16, 2026
… Re2Options (#949)

create_nfa() tied the conversion and its post-processing together
through positional booleans: it always trimmed the result, removed
epsilon transitions unless use_epsilon was set, and reduced it with
use_reduce, so a caller could not get a raw conversion out of the
parser, and builder::create_from_regex() and the Python from_regex()
exposed only the reduction.

The new Re2Options struct (keep_epsilon, epsilon, encoding, trim,
reduce) is taken by new overloads of create_nfa() and
builder::create_from_regex(). Its defaults reproduce the previous
behaviour, and the existing overloads forward to it with a trimmed
result, so no current caller changes. The Python from_regex() gains
the trim and keep_epsilon keyword arguments, with defaults matching
the previous behaviour.

Part of #793.

```cpp
struct Re2Options {
	bool keep_epsilon{false};
	Symbol epsilon{306};
	Encoding encoding{Encoding::Latin1};
	bool trim{true};
	bool reduce{true};
};
nfa::Nfa create_nfa(const std::string& pattern, const Re2Options& options);
Nfa builder::create_from_regex(const std::string& regex, const parser::Re2Options& options);
```

```python
parser.from_regex(regex, encoding='utf-8', reduce=True, trim=True, keep_epsilon=False)
```

The existing `create_nfa(pattern, use_epsilon, epsilon_value,
use_reduce, encoding)` overloads and `create_from_regex(regex, bool
reduce = true)` are unchanged and keep their behaviour.

### Verification

- C++ test `mata::parser::create_nfa() post-processing options`: the
defaults give the same automaton as the old overloads, both directly and
through `create_from_regex()`; `keep_epsilon` keeps epsilon transitions
labelled with the requested symbol (500), and none labelled 306; `trim =
false` keeps the useless state of `a[^\x00-\xff]`; `encoding` reaches
the parser (UTF-8 `\x{100}` is `c4 80`, and Latin1 rejects it).
- Python test `test_post_processing_options` covers `keep_epsilon` and
`trim`.
- This PR changes only the API, so there is no performance claim. The
conversion pipeline itself is the same.

### Stack

Second of three PRs for #793. Based on #948 (opcode handling), so the
base of this PR is that branch. GitHub retargets it to `devel` once #948
is merged.

Overlap:
- open PR #939 changes casts in the `start_state` lines of
`convert_pro_to_nfa`, which the third PR of this stack removes;
- #877 edits other declarations in `parser.pxd`;
- #895 edits `from_mata` in `parser.pyx`, next to `from_regex`;
- #883 and #877 touch `src/nfa/builder.cc` in other functions.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant