Skip to content

Harden the OpenQASM 2.0 parser against malformed input - #89

Open
mizuta-c5 wants to merge 1 commit into
softwareQinc:mainfrom
mizuta-c5:harden-openqasm-parser
Open

Harden the OpenQASM 2.0 parser against malformed input#89
mizuta-c5 wants to merge 1 commit into
softwareQinc:mainfrom
mizuta-c5:harden-openqasm-parser

Conversation

@mizuta-c5

Copy link
Copy Markdown

Harden the OpenQASM 2.0 parser against malformed input

Summary

The qasmtools recursive-descent parser can be made to abort or hang the host
process on malformed OpenQASM input reachable through the public entry point
parse_string (and therefore pystaq.parse_str, the staq CLI, and any project
embedding the header-only parser). This PR closes five such cases without changing
behaviour on valid input.

Input Before Cause
integer literal > INT_MAX abort() uncaught std::out_of_range from std::stoi
real literal out of float range (1e99999999) abort() uncaught std::out_of_range from std::stof
deeply nested (((…))) in an expression SIGSEGV unbounded recursion in parse_exp/parse_atom
unterminated string at EOF (include "…) infinite loop lex_string loop condition omits EOF
wrong token where an id/int is expected (qreg 5[2];) abort() std::bad_variant_access from as_string()/as_int() on a mismatched token

The 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 only
ParseError still aborts. The recursion and infinite-loop cases bypass exception
handling entirely (including pybind11's translation), so a pystaq-based service
crashes or hangs too.

Changes

  • lexer.hpp — wrap std::stoi/std::stof in try/catch (std::out_of_range)
    and return a Token::Kind::error (mirroring the existing unmatched-quote path);
    add && buf_->peek() != EOF to the lex_string loop (the same guard already used
    by 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 raise ParseError.
  • token.hpp — make as_int/as_real/as_string total using std::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 in ParseError.
  • CHANGES.md — one line under # Pre-release.

Testing

  • Each of the five inputs above now returns gracefully (ParseError or a clean
    parse) instead of aborting/hanging.
  • All 82 sample .qasm files under misc/ and pystaq/ parse identically before
    and after this change — no behavioural change on valid input.

Total diff: 4 files, +49/-9.

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.
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