11private import cpp
22private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
33private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
4- private import experimental.semmle.code.cpp.rangeanalysis.BinaryOrAssignOperation
5-
6- /**
7- * The current implementation for `BitwiseAndExpr` only handles cases where both operands are
8- * either unsigned or non-negative constants. This class not only covers these cases, but also
9- * adds support for `&` expressions between a signed integer with a non-negative range and a
10- * non-negative constant. It also adds support for `&=` for the same set of cases as `&`.
11- */
12- private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
13- BinaryOrAssignConstantBitwiseAndExpr e ;
14-
15- ConstantBitwiseAndExprRange ( ) { this = e .getOperation ( ) }
16-
17- BinaryOrAssignConstantBitwiseAndExpr getExpr ( ) { result = e }
18-
19- Expr getLeftOperand ( ) { result = e .getLeftOperand ( ) }
20-
21- Expr getRightOperand ( ) { result = e .getRightOperand ( ) }
22-
23- override float getLowerBounds ( ) { result = e .getLowerBounds ( ) }
24-
25- override float getUpperBounds ( ) { result = e .getUpperBounds ( ) }
26-
27- override predicate dependsOnChild ( Expr child ) { child = e .getAnOperand ( ) }
28- }
29-
30- private class ConstantBitwiseAndExprOp extends Expr {
31- BinaryOrAssignConstantBitwiseAndExpr b ;
32- float lowerBound ;
33- float upperBound ;
34-
35- ConstantBitwiseAndExprOp ( ) {
36- this = b .getAnOperand ( ) and
37- lowerBound = getFullyConvertedLowerBounds ( this ) and
38- upperBound = getFullyConvertedUpperBounds ( this ) and
39- lowerBound <= upperBound
40- }
41-
42- float getLowerBound ( ) { result = lowerBound }
43-
44- float getUpperBound ( ) { result = upperBound }
45-
46- predicate hasNegativeRange ( ) { getLowerBound ( ) < 0 or getUpperBound ( ) < 0 }
47- }
484
495/**
506 * Holds if `e` is a constant or if it is a variable with a constant value
@@ -58,32 +14,49 @@ float evaluateConstantExpr(Expr e) {
5814 )
5915}
6016
61- private class BinaryOrAssignConstantBitwiseAndExpr extends BinaryOrAssignOperation {
62- BinaryOrAssignConstantBitwiseAndExpr ( ) {
63- (
64- getOperation ( ) instanceof BitwiseAndExpr
17+ /**
18+ * The current implementation for `BitwiseAndExpr` only handles cases where both operands are
19+ * either unsigned or non-negative constants. This class not only covers these cases, but also
20+ * adds support for `&` expressions between a signed integer with a non-negative range and a
21+ * non-negative constant. It also adds support for `&=` for the same set of cases as `&`.
22+ */
23+ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
24+ ConstantBitwiseAndExprRange ( ) {
25+ exists ( Expr l , Expr r |
26+ l = this .( BitwiseAndExpr ) .getLeftOperand ( ) and
27+ r = this .( BitwiseAndExpr ) .getRightOperand ( )
6528 or
66- getOperation ( ) instanceof AssignAndExpr
67- ) and
68- // Make sure all operands and the result type are integral
69- getOperation ( ) .getUnspecifiedType ( ) instanceof IntegralType and
70- getLeftOperand ( ) .getUnspecifiedType ( ) instanceof IntegralType and
71- getRightOperand ( ) .getUnspecifiedType ( ) instanceof IntegralType and
72- // No operands can be negative constants
73- not ( evaluateConstantExpr ( getLeftOperand ( ) ) < 0 or evaluateConstantExpr ( getRightOperand ( ) ) < 0 ) and
74- // At least one operand must be a non-negative constant
75- ( evaluateConstantExpr ( getLeftOperand ( ) ) >= 0 or evaluateConstantExpr ( getRightOperand ( ) ) >= 0 )
29+ l = this .( AssignAndExpr ) .getLValue ( ) and
30+ r = this .( AssignAndExpr ) .getRValue ( )
31+ |
32+ // No operands can be negative constants
33+ not ( evaluateConstantExpr ( getLOp ( this ) ) < 0 or evaluateConstantExpr ( getROp ( this ) ) < 0 ) and
34+ // At least one operand must be a non-negative constant
35+ ( evaluateConstantExpr ( getLOp ( this ) ) >= 0 or evaluateConstantExpr ( getROp ( this ) ) >= 0 )
36+ )
7637 }
7738
78- float getLowerBounds ( ) {
79- // If both operands have non-negative ranges, the lower bound is zero. If an operand can have
80- // negative values, the lower bound is unconstrained.
81- exists ( ConstantBitwiseAndExprOp l , ConstantBitwiseAndExprOp r |
82- l = getLeftOperand ( ) and
83- r = getRightOperand ( ) and
39+ Expr getLeftOperand ( ) {
40+ result = this .( BitwiseAndExpr ) .getLeftOperand ( ) or
41+ result = this .( AssignAndExpr ) .getLValue ( )
42+ }
43+
44+ Expr getRightOperand ( ) {
45+ result = this .( BitwiseAndExpr ) .getRightOperand ( ) or
46+ result = this .( AssignAndExpr ) .getRValue ( )
47+ }
48+
49+ override float getLowerBounds ( ) {
50+ // If an operand can have negative values, the lower bound is unconstrained.
51+ // Otherwise, the lower bound is zero.
52+ exists ( float lLower , float lUpper , float rLower , float rUpper |
53+ lLower = getFullyConvertedLowerBounds ( getLeftOperand ( ) ) and
54+ lUpper = getFullyConvertedUpperBounds ( getLeftOperand ( ) ) and
55+ rLower = getFullyConvertedLowerBounds ( getRightOperand ( ) ) and
56+ rUpper = getFullyConvertedUpperBounds ( getRightOperand ( ) ) and
8457 (
85- ( l . hasNegativeRange ( ) or r . hasNegativeRange ( ) ) and
86- result = exprMinVal ( getOperation ( ) )
58+ ( lLower < 0 or rLower < 0 ) and
59+ result = exprMinVal ( this )
8760 or
8861 // This technically results in two lowerBounds when an operand range is negative, but
8962 // that's fine since `exprMinVal(x) <= 0`. We can't use an if statement here without
@@ -93,20 +66,27 @@ private class BinaryOrAssignConstantBitwiseAndExpr extends BinaryOrAssignOperati
9366 )
9467 }
9568
96- float getUpperBounds ( ) {
69+ override float getUpperBounds ( ) {
9770 // If an operand can have negative values, the upper bound is unconstrained.
98- // Otherwise, the upper bound is the maximum of the upper bounds of the operands
99- exists ( ConstantBitwiseAndExprOp l , ConstantBitwiseAndExprOp r |
100- l = getLeftOperand ( ) and
101- r = getRightOperand ( ) and
71+ // Otherwise, the upper bound is the minimum of the upper bounds of the operands
72+ exists ( float lLower , float lUpper , float rLower , float rUpper |
73+ lLower = getFullyConvertedLowerBounds ( getLeftOperand ( ) ) and
74+ lUpper = getFullyConvertedUpperBounds ( getLeftOperand ( ) ) and
75+ rLower = getFullyConvertedLowerBounds ( getRightOperand ( ) ) and
76+ rUpper = getFullyConvertedUpperBounds ( getRightOperand ( ) ) and
10277 (
103- ( l . hasNegativeRange ( ) or r . hasNegativeRange ( ) ) and
104- result = exprMaxVal ( getOperation ( ) )
78+ ( lLower < 0 or rLower < 0 ) and
79+ result = exprMaxVal ( this )
10580 or
10681 // This technically results in two upperBounds when an operand range is negative, but
107- // that's fine since `exprMaxVal(b) >= result`
108- result = r .getUpperBound ( ) .minimum ( l .getUpperBound ( ) )
82+ // that's fine since `exprMaxVal(b) >= result`. We can't use an if statement here without
83+ // non-monotonic recursion issues
84+ result = rUpper .minimum ( lUpper )
10985 )
11086 )
11187 }
88+
89+ override predicate dependsOnChild ( Expr child ) {
90+ child = getLeftOperand ( ) or child = getRightOperand ( )
91+ }
11292}
0 commit comments