Harden the OpenQASM 2.0 parser against malformed input - #89
Open
mizuta-c5 wants to merge 1 commit into
Open
Conversation
Five short malformed OpenQASM inputs reach the public entry point parse_string (and thus pystaq.parse_str, the staq CLI, and any embedder) and abort or hang the host process: - out-of-range integer/real literals: uncaught std::out_of_range from std::stoi/std::stof (SIGABRT) - deeply nested parenthesised expressions: unbounded recursion in parse_exp/parse_atom (stack overflow, SIGSEGV) - unterminated string literal at EOF: lex_string loop omits EOF (infinite loop, unbounded memory growth) - token kind/value mismatch (e.g. `qreg 5[2];`): as_string/as_int on a mismatched token throws std::bad_variant_access (SIGABRT) The exception cases bypass the parser's documented ParseError channel, so a caller catching only ParseError still aborts; the recursion and infinite loop bypass exception handling entirely, so a pystaq-based service crashes or hangs too. Wrap stoi/stof in try/catch and emit a Token::Kind::error; add an EOF guard to the lex_string loop; bound expression recursion with an RAII depth guard (MAX_EXPR_DEPTH); make as_int/as_real/as_string total via std::get_if. Each input now yields a parse error instead of aborting or hanging, and all 82 bundled sample .qasm files parse identically before and after.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Harden the OpenQASM 2.0 parser against malformed input
Summary
The
qasmtoolsrecursive-descent parser can be made to abort or hang the hostprocess on malformed OpenQASM input reachable through the public entry point
parse_string(and thereforepystaq.parse_str, thestaqCLI, and any projectembedding the header-only parser). This PR closes five such cases without changing
behaviour on valid input.
> INT_MAXabort()std::out_of_rangefromstd::stoifloatrange (1e99999999)abort()std::out_of_rangefromstd::stof(((…)))in an expressionparse_exp/parse_atominclude "…)lex_stringloop condition omits EOFqreg 5[2];)abort()std::bad_variant_accessfromas_string()/as_int()on a mismatched tokenThe first, second and fifth cases matter beyond a CLI: they raise C++ exceptions
that are not the parser's documented
ParseError, so a caller catching onlyParseErrorstill aborts. The recursion and infinite-loop cases bypass exceptionhandling entirely (including pybind11's translation), so a
pystaq-based servicecrashes or hangs too.
Changes
lexer.hpp— wrapstd::stoi/std::stofintry/catch (std::out_of_range)and return a
Token::Kind::error(mirroring the existing unmatched-quote path);add
&& buf_->peek() != EOFto thelex_stringloop (the same guard already usedby the comment lexer).
parser.hpp— bound expression-parsing recursion with an RAII depth guard(
MAX_EXPR_DEPTH = 1000); on overflow, set the error flag and raiseParseError.token.hpp— makeas_int/as_real/as_stringtotal usingstd::get_if,returning a default on a kind/value mismatch instead of throwing. The parser's
error_flag is set independently, so malformed input still ends inParseError.CHANGES.md— one line under# Pre-release.Testing
ParseErroror a cleanparse) instead of aborting/hanging.
.qasmfiles undermisc/andpystaq/parse identically beforeand after this change — no behavioural change on valid input.
Total diff: 4 files, +49/-9.