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
C++: Parameterize the semantic range analysis #11728
Conversation
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Fixed
Show fixed
Hide fixed
|
This looks great. Looking forward to seeing this PR pulled out of draft! |
3774db7
to
452d438
Compare
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysis.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisSpecific.qll
Fixed
Show fixed
Hide fixed
d2d62ad
to
35ecf55
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! If DCA is happy (make sure to run the memory-corruption query pack) then I think this should be pretty safe to merge
| SemReason reason | ||
| ) { | ||
| not Specific::ignoreExprBound(e) and | ||
| //not Specific::mayOverflow(e, upper.booleanNot()) and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing this is a subtle hint to the next PR?
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Outdated
Show resolved
Hide resolved
| predicate mayOverflow(SemExpr e, boolean positively) { | ||
| exists(IR::Instruction i | | ||
| i = e and | ||
| ( | ||
| positively = true and | ||
| Simple::exprMightOverflowPositively(i.getConvertedResultExpression()) | ||
| or | ||
| positively = false and | ||
| Simple::exprMightOverflowNegatively(i.getConvertedResultExpression()) | ||
| ) | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you intent to commit this?
| @@ -3,86 +3,105 @@ | |||
| */ | |||
|
|
|||
| private import experimental.semmle.code.cpp.semantic.Semantic | |||
| private import RangeAnalysisStage | |||
| private import semmle.code.cpp.ir.IR as IR | |||
| private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis as Simple | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you intent to import SimpleRangeAnalysis here?
| private module SignAnalysisInstantiated = SignAnalysis<D, UtilParam>; // TODO: will this cause reevaluation if it's instantiated with the same DeltaSig and UtilParam multiple times? | ||
|
|
||
| private import SignAnalysisInstantiated | ||
|
|
||
| private module ModulusAnalysisInstantiated = ModulusAnalysis<D, UtilParam>; // TODO: will this cause reevaluation if it's instantiated with the same DeltaSig and UtilParam multiple times? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A note for anyone else reading this: We discussed in our 1:1 that we believe this should not cause re-evaluation because modules are applicative. But we should still make sure that we don't get re-evaluation due to different optimisation decisions.
| @@ -157,7 +186,7 @@ module RangeStage<RangeSig RangeParam> { | |||
| */ | |||
| cached | |||
| predicate possibleReason(SemGuard guard) { | |||
| guard = boundFlowCond(_, _, _, _, _) or guard = semEqFlowCond(_, _, _, _, _) | |||
| guard = boundFlowCond(_, _, _, _, _) or guard = UtilParam::semEqFlowCond(_, _, _, _, _) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we privately import UtilParam inside the RangeStage module to avoid prefixing with UtilParam? (And similarly for the other module parameters.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was the conclusion here? Is this possible, or not? Or is this not something we want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's done now - see this commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in ModulusAnalysis, where I'd also expect it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this again. I'm somewhat split on this. On the one hand, having the imports makes the code more compact. On the other hand, without the imports it's clearer where things are coming from. So I'm fine with leaving things as-is in the modulus analysis.
| @@ -146,7 +146,7 @@ module ModulusAnalysis<DeltaSig D, UtilSig<D> U> { | |||
| private predicate phiSelfModulus( | |||
| SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod | |||
| ) { | |||
| exists(SemSsaBound phibound, int v, int m | | |||
| exists(Bounds::SemSsaBound phibound, int v, int m | | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, it would be awesome if we could just import Bounds to avoid all these prefixes
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SimpleRangeAnalysisWrapper.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SimpleRangeAnalysisWrapper.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SimpleRangeAnalysisWrapper.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SimpleRangeAnalysisWrapper.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SimpleRangeAnalysisWrapper.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SimpleRangeAnalysisWrapper.qll
Fixed
Show fixed
Hide fixed
a985ccf
to
a7e5e55
Compare
|
Rebased away the overflow-related changes. I'll import the module params as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodeQL found more than 10 potential problems in the proposed changes. Check the Files changed tab for more details.
be5ff77
to
359b428
Compare
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Fixed
Show fixed
Hide fixed
| class UseSignExpr extends FlowSignExpr { | ||
| SemSsaVariable v; | ||
| /** A binary expression whose sign is computed from the signs of its operands. */ | ||
| private class BinarySignExpr extends FlowSignExpr { |
Check warning
Code scanning / CodeQL
Suggest using non-extending subtype relationships.
| private class UnarySignExpr extends FlowSignExpr { | ||
| SemUnaryExpr unary; | ||
| /** A unary expression whose sign is computed from the sign of its operand. */ | ||
| private class UnarySignExpr extends FlowSignExpr { |
Check warning
Code scanning / CodeQL
Suggest using non-extending subtype relationships.
| * A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on | ||
| * the sign of its operand and the source and destination types. | ||
| */ | ||
| abstract private class CastSignExpr extends FlowSignExpr { |
Check warning
Code scanning / CodeQL
Suggest using non-extending subtype relationships.
| not Lang::ignoreSsaReadArithmeticExpr(result) | ||
| ) | ||
| or | ||
| exists(SemSubExpr sub, float d1, SemConstantIntegerExpr c | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this suddenly float and not D::Delta?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It needs to be either float or int so that we can do the addition on line 29
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So when should I expect something from DeltaSig and when should I expect and int or a float? And, what determines the choice between int and float?
The answers to both these questions should probably be documented as part of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed in our sync: prefer delta, but unwrap when needing to do an arithmetic operation. Prefer unwrapping to float, except in the modulus analysis where we unwrap to int (so we can do the modulus analysis).
The answers to both these questions should probably be documented as part of the code.
I don't think this is necessary, but a qldoc string on DeltaSig is still appreciated.
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Show resolved
Hide resolved
| result = sub and | ||
| sub.getLeftOperand() = semSsaRead(v, D::fromFloat(d1)) and | ||
| sub.getRightOperand() = c and | ||
| delta = D::fromFloat(d1 + c.getIntValue()) and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed in sync: I was somewhat confused here, as the previous case is more "delta"-heavy. To match that I would expect the following here:
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue()) and
(with the declaration of d1 changed accordingly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One final question below, otherwise this looks like a pretty straightforward parameterization.
QLDoc checks are currently failing, so that probably still needs to be resolved.
I'm not sure I'm looking at the correct DCA experiment, but the last one seems to show a quite significant slowdown.
| @@ -0,0 +1,25 @@ | |||
| import cpp | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing from this file is being used elsewhere yet. Do I see that correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, didn't mean to include that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of comments. Otherwise this LGTM! We should also do another DCA run to make sure that the crazy performance regressions are gone.
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/FloatDelta.qll
Outdated
Show resolved
Hide resolved
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Outdated
Show resolved
Hide resolved
This makes the modulus analysis and sign analysis into parameterized modules which are instantiated in the main range analysis module, and makes RangeAnalysisSpecific and RangeUtils into parameters to the main range analysis. Some classes also need to be moved and made into `instanceof` extensions because they'd otherwise be extending across parameterized module boundaries.
These now instantiate their respective parameterized modules. No results change.
c93c804
to
337a747
Compare
|
@rdmarsh2 I tried to fix the merge conflict so that I could start another DCA run on this, but it's not quite trivial enough for me to solve it. Do you mind taking a look at it? |
| @@ -3,86 +3,91 @@ | |||
| */ | |||
|
|
|||
| private import experimental.semmle.code.cpp.semantic.Semantic | |||
| private import RangeAnalysisStage | |||
| private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta | |||
| private import experimental.semmle.code.cpp.semantic.analysis.IntDelta | |||
Check warning
Code scanning / CodeQL
QL-for-QL encountered an internal consistency error
| @@ -3,86 +3,91 @@ | |||
| */ | |||
|
|
|||
| private import experimental.semmle.code.cpp.semantic.Semantic | |||
| private import RangeAnalysisStage | |||
| private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta | |||
| private import experimental.semmle.code.cpp.semantic.analysis.IntDelta | |||
Check warning
Code scanning / CodeQL
QL-for-QL encountered an internal consistency error
| * - `upper = false` : `inp >= b + delta` | ||
| */ | ||
| pragma[noinline] | ||
| private predicate boundedPhiCand( |
Check warning
Code scanning / CodeQL
Candidate predicate not marked as `nomagic`
| if semNegative(x) | ||
| then upper = true and delta = D::fromInt(0) | ||
| else none() |
Check warning
Code scanning / CodeQL
Use of 'if' with a 'none()' branch.
| if semNegative(x) | ||
| then upper = false and delta = D::fromInt(0) | ||
| else none() |
Check warning
Code scanning / CodeQL
Use of 'if' with a 'none()' branch.
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Fixed
Show fixed
Hide fixed
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll
Fixed
Show fixed
Hide fixed
Conflicting change to boundedPhiInp copied to RangeAnalysisStage.qll
1969c9a
to
601b43a
Compare
cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisSpecific.qll
Outdated
Show resolved
Hide resolved
…eAnalysisSpecific.qll
|
DCA looks good, and the CI failure is unrelated to this PR (it would be fixed by merging in |
This PR makes the semantic range analysis into a parameterized module, with parameters for the type of deltas, the Bound type, the language-specific predicates in RangeAnalysisSpecific.qll, and the utility predicates in RangeUtil.qll. ModulusAnalysis and SignAnalysis are also parameterized, but are instantiated by the main range analysis module. The range analysis is not yet parameterized over the remainder of the Semantic module. This change should be behavior-preserving - the parameterized module is in the new
RangeAnalysisStage.qllfile, with an instantiation inRangeAnalysis.qll