Skip to content

Avoid using string parsing for ParameterExpression.sympify() #14391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 21, 2025

Conversation

mtreinish
Copy link
Member

Summary

In the recently merged #13278 the implementation for the sympify method for ParameterExpression was changed because we no longer rely on symengine internally. Previously we would just return the inner symengine object used to represent the symbolic expression. Without symengine available #13278 updated the implementation of the method to generate a string representation of the expression and pass that to sympify which has a "parser" for converting that expression string to a sympy object. However, sympify() method is insecure as it internally relies on Python's eval() to parse the string and can't be used for untrusted input. While this doesn't have the same exact exposure as in GHSA-6m2c-76ff-6vrf because you have to opt-in to using this function with input that is untrusted and the degrees of freedom are less because it has to go through the rust symbolic expression it is still a potential vulnerability waiting to happen. This commit reworks the sympify implementation to avoid using sympy's parser and instead just builds the sympy expression from the internal state directly.

Details and comments

In the recently merged Qiskit#13278 the implementation for the sympify
method for ParameterExpression was changed because we no longer rely on
symengine internally. Previously we would just return the inner
symengine object used to represent the symbolic expression. Without
symengine available Qiskit#13278 updated the implementation of the method to
generate a string representation of the expression and pass that to
sympify which has a "parser" for converting that expression string to a
sympy object. However, sympify() method is insecure as it internally
relies on Python's eval() to parse the string and can't be used for
untrusted input. While this doesn't have the same exact exposure as
in GHSA-6m2c-76ff-6vrf
because you have to opt-in to using this function with input that is
untrusted and the degrees of freedom are less because it has to go
through the rust symbolic expression it is still a potential
vulnerability waiting to happen. This commit reworks the sympify
implementation to avoid using sympy's parser and instead just builds
the sympy expression from the internal state directly.
@mtreinish mtreinish added this to the 2.1.0 milestone May 16, 2025
@mtreinish mtreinish requested a review from a team as a code owner May 16, 2025 19:28
@mtreinish mtreinish added the Changelog: None Do not include in changelog label May 16, 2025
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

@coveralls
Copy link

coveralls commented May 16, 2025

Pull Request Test Coverage Report for Build 15165457978

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 47 of 47 (100.0%) changed or added relevant lines in 2 files are covered.
  • 10 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+0.02%) to 88.31%

Files with Coverage Reduction New Missed Lines %
crates/circuit/src/symbol_expr.rs 1 74.98%
crates/qasm2/src/lex.rs 3 92.73%
crates/qasm2/src/parse.rs 6 97.61%
Totals Coverage Status
Change from base Build 15162616641: 0.02%
Covered Lines: 78366
Relevant Lines: 88740

💛 - Coveralls

Copy link
Contributor

@Cryoris Cryoris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks much cleaner than relying on sympify 👍🏻 Would it make sense to add some concrete test cases, or do you think this is sufficiently tested implicitly? Right now I think there's only a single test that calls sympify directly, and it's not comparing to a hardcoded SymPy expression.

@mtreinish
Copy link
Member Author

I'll add some specific tests, there is definitely some coverage from template optimization. It caught some bugs, but I can reuse the circuits from the qpy tests and test sympify() explicitly.

@mtreinish
Copy link
Member Author

Test added in: 39e360a it caught several issues with the implementation

Copy link
Contributor

@Cryoris Cryoris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Cryoris Cryoris enabled auto-merge May 21, 2025 15:05
@Cryoris Cryoris added this pull request to the merge queue May 21, 2025
Merged via the queue into Qiskit:main with commit 8970b83 May 21, 2025
24 checks passed
rahaman-quantum pushed a commit to rahaman-quantum/qiskit that referenced this pull request Jun 20, 2025
…14391)

* Avoid using string parsing for ParameterExpression.sympify()

In the recently merged Qiskit#13278 the implementation for the sympify
method for ParameterExpression was changed because we no longer rely on
symengine internally. Previously we would just return the inner
symengine object used to represent the symbolic expression. Without
symengine available Qiskit#13278 updated the implementation of the method to
generate a string representation of the expression and pass that to
sympify which has a "parser" for converting that expression string to a
sympy object. However, sympify() method is insecure as it internally
relies on Python's eval() to parse the string and can't be used for
untrusted input. While this doesn't have the same exact exposure as
in GHSA-6m2c-76ff-6vrf
because you have to opt-in to using this function with input that is
untrusted and the degrees of freedom are less because it has to go
through the rust symbolic expression it is still a potential
vulnerability waiting to happen. This commit reworks the sympify
implementation to avoid using sympy's parser and instead just builds
the sympy expression from the internal state directly.

* Remove unused Rust functions that support sympy string generation

* Add test coverage for all of parameter expression

* Add .sign() to the megaexpression
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants