|
18 | 18 |
|
19 | 19 | import cpp |
20 | 20 | import semmle.code.cpp.controlflow.SSA |
| 21 | +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis |
| 22 | +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils |
21 | 23 |
|
22 | 24 | /** |
23 | 25 | * Holds if `e` is either: |
@@ -64,29 +66,105 @@ int getEffectiveMulOperands(MulExpr me) { |
64 | 66 | ) |
65 | 67 | } |
66 | 68 |
|
67 | | -class LeafExpr extends Expr { |
68 | | - LeafExpr() { not this instanceof BinaryOperation } |
| 69 | +/** |
| 70 | + * As SimpleRangeAnalysis does not support reasoning about multiplication |
| 71 | + * we create a tiny abstract interpreter for handling multiplication, which |
| 72 | + * we invoke only after weeding out of all of trivial cases that we do |
| 73 | + * not care about. By default, the maximum and minimum values are computed |
| 74 | + * using SimpleRangeAnalysis. |
| 75 | + */ |
| 76 | +class AnalyzableExpr extends Expr { |
| 77 | + float maxValue() { result = upperBound(this.getFullyConverted()) } |
| 78 | + |
| 79 | + float minValue() { result = lowerBound(this.getFullyConverted()) } |
| 80 | +} |
| 81 | + |
| 82 | +class ParenAnalyzableExpr extends AnalyzableExpr, ParenthesisExpr { |
| 83 | + override float maxValue() { result = this.getExpr().(AnalyzableExpr).maxValue() } |
| 84 | + |
| 85 | + override float minValue() { result = this.getExpr().(AnalyzableExpr).minValue() } |
| 86 | +} |
| 87 | + |
| 88 | +class MulAnalyzableExpr extends AnalyzableExpr, MulExpr { |
| 89 | + override float maxValue() { |
| 90 | + exists(float x1, float y1, float x2, float y2 | |
| 91 | + x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and |
| 92 | + x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and |
| 93 | + y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and |
| 94 | + y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and |
| 95 | + result = (x1 * y1).maximum(x1 * y2).maximum(x2 * y1).maximum(x2 * y2) |
| 96 | + ) |
| 97 | + } |
| 98 | + |
| 99 | + override float minValue() { |
| 100 | + exists(float x1, float x2, float y1, float y2 | |
| 101 | + x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and |
| 102 | + x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and |
| 103 | + y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and |
| 104 | + y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and |
| 105 | + result = (x1 * y1).minimum(x1 * y2).minimum(x2 * y1).minimum(x2 * y2) |
| 106 | + ) |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +class AddAnalyzableExpr extends AnalyzableExpr, AddExpr { |
| 111 | + override float maxValue() { |
| 112 | + result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() + |
| 113 | + this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() |
| 114 | + } |
| 115 | + |
| 116 | + override float minValue() { |
| 117 | + result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() + |
| 118 | + this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +class SubAnalyzableExpr extends AnalyzableExpr, SubExpr { |
| 123 | + override float maxValue() { |
| 124 | + result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() - |
| 125 | + this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() |
| 126 | + } |
| 127 | + |
| 128 | + override float minValue() { |
| 129 | + result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() - |
| 130 | + this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +class VarAnalyzableExpr extends AnalyzableExpr, VariableAccess { |
| 135 | + override float maxValue() { |
| 136 | + exists(SsaDefinition def, Variable v | |
| 137 | + def.getAUse(v) = this and |
| 138 | + // if there is a defining expression, use that for |
| 139 | + // computing the maximum value. Otherwise, assign the |
| 140 | + // variable the largest possible value it can hold |
| 141 | + if exists(def.getDefiningValue(v)) |
| 142 | + then result = def.getDefiningValue(v).(AnalyzableExpr).maxValue() |
| 143 | + else result = exprMaxVal(this) |
| 144 | + ) |
| 145 | + } |
| 146 | + |
| 147 | + override float minValue() { |
| 148 | + exists(SsaDefinition def, Variable v | |
| 149 | + def.getAUse(v) = this and |
| 150 | + if exists(def.getDefiningValue(v)) |
| 151 | + then result = def.getDefiningValue(v).(AnalyzableExpr).minValue() |
| 152 | + else result = exprMinVal(this) |
| 153 | + ) |
| 154 | + } |
69 | 155 | } |
70 | 156 |
|
71 | 157 | /** |
72 | | - * Gets the size of the largest type (in bytes) of an non-literal |
73 | | - * operand to a binary operation. For example, for |
74 | | - * ``` |
75 | | - * unsigned char a; short b; |
76 | | - * (a + 1) * b; |
77 | | - * ``` |
78 | | - * the result is 2 (i.e., the size of a `short`) |
| 158 | + * Holds if `t` is not an instance of `IntegralType`, |
| 159 | + * or if `me` cannot be proven to not overflow |
79 | 160 | */ |
80 | | -int widenedFromLiteralFromSize(Expr e) { |
81 | | - exists(Literal lit | |
82 | | - lit = e.(BinaryOperation).getAnOperand+() and |
83 | | - result = max(LeafExpr other | |
84 | | - other = e.(BinaryOperation).getAnOperand+() and |
85 | | - not other instanceof Literal |
86 | | - | |
87 | | - other.getType().getSize() |
88 | | - ) and |
89 | | - result < lit.getType().getSize() |
| 161 | +predicate overflows(MulExpr me, Type t) { |
| 162 | + t instanceof IntegralType |
| 163 | + implies |
| 164 | + ( |
| 165 | + me.(MulAnalyzableExpr).maxValue() > exprMaxVal(me) |
| 166 | + or |
| 167 | + me.(MulAnalyzableExpr).minValue() < exprMinVal(me) |
90 | 168 | ) |
91 | 169 | } |
92 | 170 |
|
@@ -128,20 +206,10 @@ where |
128 | 206 | ) and |
129 | 207 | e.(Literal).getType().getSize() = t2.getSize() |
130 | 208 | ) and |
131 | | - // exclude cases where the operands to the multiplication were |
132 | | - // small, but widened due to a literal in a subexpression. |
133 | | - // For instance, if the multiplication is |
134 | | - // ``` |
135 | | - // int c = (a + 1) * (b + 1) |
136 | | - // ``` |
137 | | - // where a and b are both unsigned chars. In this case the maximum |
138 | | - // value of c will be 256 * 256, even though the arguments to the |
139 | | - // multiplication are both typed as int. |
140 | | - not exists(Expr e1, Expr e2 | |
141 | | - e1 = me.getLeftOperand() and |
142 | | - e2 = me.getRightOperand() and |
143 | | - widenedFromLiteralFromSize(e1) + widenedFromLiteralFromSize(e2) < t1.getSize() |
144 | | - ) |
| 209 | + // only report if cannot prove that the result of the |
| 210 | + // multiplication will be less (resp. greater) than the |
| 211 | + // maximum (resp. minimum) number we can store. |
| 212 | + overflows(me, t1) |
145 | 213 | select me, |
146 | 214 | "Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '" |
147 | 215 | + me.getFullyConverted().getType().toString() + "'." |
0 commit comments