@@ -17,11 +17,12 @@ float getMaxIntValue(int bitSize, boolean isSigned) {
17
17
* Get the size of `int` or `uint` in `file`, or 0 if it is
18
18
* architecture-specific.
19
19
*/
20
- int getIntTypeBitSize ( File file ) {
20
+ bindingset [ architectureSpecificBitSize]
21
+ int getIntTypeBitSize ( File file , int architectureSpecificBitSize ) {
21
22
file .constrainsIntBitSize ( result )
22
23
or
23
24
not file .constrainsIntBitSize ( _) and
24
- result = 0
25
+ result = architectureSpecificBitSize
25
26
}
26
27
27
28
/**
@@ -90,7 +91,7 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
90
91
) and
91
92
(
92
93
if apparentBitSize = 0
93
- then effectiveBitSize = getIntTypeBitSize ( source .getFile ( ) )
94
+ then effectiveBitSize = getIntTypeBitSize ( source .getFile ( ) , 0 )
94
95
else effectiveBitSize = apparentBitSize
95
96
) and
96
97
// `effectiveBitSize` could be any value between 0 and 64, but we
@@ -113,7 +114,7 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
113
114
bitSize = integerType .getSize ( )
114
115
or
115
116
not exists ( integerType .getSize ( ) ) and
116
- bitSize = getIntTypeBitSize ( sink .getFile ( ) )
117
+ bitSize = getIntTypeBitSize ( sink .getFile ( ) , 0 )
117
118
) and
118
119
if integerType instanceof SignedIntegerType then sinkIsSigned = true else sinkIsSigned = false
119
120
) and
@@ -152,50 +153,73 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
152
153
153
154
/** Flow state for ConversionWithoutBoundsCheckConfig. */
154
155
newtype IntegerConversionFlowState =
155
- /** Keep track of info about the source and potential sinks. */
156
- TFlowstate ( boolean sinkIsSigned , int sourceBitSize , int sinkBitSize ) {
157
- sinkIsSigned in [ true , false ] and
158
- isIncorrectIntegerConversion ( sourceBitSize , sinkBitSize )
156
+ TFlowstate ( int bitSize , boolean isSigned ) {
157
+ bitSize = [ 0 , 8 , 16 , 32 , 64 ] and
158
+ isSigned = [ true , false ]
159
159
}
160
160
161
- /** Gets whether the sink is signed. */
162
- boolean getSinkIsSigned ( IntegerConversionFlowState state ) { state = TFlowstate ( result , _, _) }
161
+ /**
162
+ * The integer type with bit size `bitSize` and signedness `isSigned` has
163
+ * maximum value `2^result - 1`.
164
+ */
165
+ bindingset [ bitSize, bitSizeForZero]
166
+ private int f ( int bitSize , boolean isSigned , int bitSizeForZero ) {
167
+ exists ( int effectiveBitSize | effectiveBitSize = replaceZeroWith ( bitSize , bitSizeForZero ) |
168
+ isSigned = true and result = effectiveBitSize - 1
169
+ or
170
+ isSigned = false and result = effectiveBitSize
171
+ )
172
+ }
163
173
164
- /** Gets the bit size of the source. */
165
- int getSourceBitSize ( IntegerConversionFlowState state ) { state = TFlowstate ( _, result , _) }
174
+ private module ConversionWithoutBoundsCheckConfig implements DataFlow:: StateConfigSig {
175
+ class FlowState extends IntegerConversionFlowState {
176
+ /** Gets the signedness of the flow state. */
177
+ boolean getIsSigned ( ) { this = TFlowstate ( _, result ) }
166
178
167
- /** Gets the bit size of the sink . */
168
- int getSinkBitSize ( IntegerConversionFlowState state ) { state = TFlowstate ( _ , _, result ) }
179
+ /** Gets the bit size of the flow state . */
180
+ int getBitSize ( ) { this = TFlowstate ( result , _) }
169
181
170
- private module ConversionWithoutBoundsCheckConfig implements DataFlow :: StateConfigSig {
171
- class FlowState = IntegerConversionFlowState ;
182
+ string toString ( ) { result = "FlowState(" + this . getBitSize ( ) + "," + this . getIsSigned ( ) + ")" }
183
+ }
172
184
173
185
predicate isSource ( DataFlow:: Node source , FlowState state ) {
174
186
exists (
175
187
DataFlow:: CallNode c , IntegerParser:: Range ip , int apparentBitSize , int effectiveBitSize
176
188
|
177
- c .getTarget ( ) = ip and source = c . getResult ( 0 )
178
- |
189
+ c .getTarget ( ) = ip and
190
+ source = c . getResult ( 0 ) and
179
191
(
180
192
apparentBitSize = ip .getTargetBitSize ( )
181
193
or
182
194
// If we are reading a variable, check if it is
183
195
// `strconv.IntSize`, and use 0 if it is.
184
- exists ( DataFlow:: Node rawBitSize | rawBitSize = ip .getTargetBitSizeInput ( ) .getNode ( c ) |
196
+ exists ( DataFlow:: Node rawBitSize |
197
+ rawBitSize = ip .getTargetBitSizeInput ( ) .getNode ( c ) and
185
198
if rawBitSize = any ( Strconv:: IntSize intSize ) .getARead ( )
186
199
then apparentBitSize = 0
187
200
else apparentBitSize = rawBitSize .getIntValue ( )
188
201
)
189
202
) and
190
- (
191
- if apparentBitSize = 0
192
- then effectiveBitSize = getIntTypeBitSize ( source .getFile ( ) )
193
- else effectiveBitSize = apparentBitSize
194
- ) and
195
- // `effectiveBitSize` could be any value between 0 and 64, but we
196
- // can round it up to the nearest size of an integer type without
197
- // changing behavior.
198
- getSourceBitSize ( state ) = min ( int b | b in [ 0 , 8 , 16 , 32 , 64 ] and b >= effectiveBitSize )
203
+ // Note that `getIntTypeBitSize` can return 0, so `effectiveBitSize`
204
+ // can be 0. Also `effectiveBitSize` is not necessarily the bit-size
205
+ // of an integer type - it can be any integer between 0 and 64.
206
+ effectiveBitSize = replaceZeroWith ( apparentBitSize , getIntTypeBitSize ( source .getFile ( ) , 0 ) ) and
207
+ // `state` represents an integer type for the sink which will overflow
208
+ // if assigned the maximum value that can be parsed by `ip`. If both the
209
+ // source and the sink are `int` or `uint` then overflow can only happen
210
+ // if we are converting from `uint` to `int`.
211
+ if state .getBitSize ( ) = 0 and effectiveBitSize = 0
212
+ then ip .isSigned ( ) = false and state .getIsSigned ( ) = true
213
+ else
214
+ // The maximum value for a signed type with n bits is 2^(n - 1) - 1.
215
+ // The maximum value for an unsigned type with n bits is 2^n - 1. To
216
+ // deal with `int` and `uint` we treat them as 64-bit if they are at
217
+ // the source (so we catch that flow from `int` to `int32` is incorrect
218
+ // on a 64-bit architecture) and 32-bit if they are at the sink (so we
219
+ // catch that flow from `int64` to `int` is incorrect on a 32-bit
220
+ // architecture), and we have already dealt with the case that both
221
+ // source and sink are `int` or `uint`.
222
+ f ( state .getBitSize ( ) , state .getIsSigned ( ) , 32 ) < f ( effectiveBitSize , ip .isSigned ( ) , 64 )
199
223
)
200
224
}
201
225
@@ -205,18 +229,18 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
205
229
* not also in a right-shift expression. We allow this case because it is
206
230
* a common pattern to serialise `byte(v)`, `byte(v >> 8)`, and so on.
207
231
*/
208
- additional predicate isSinkWithBitSize (
209
- DataFlow:: TypeCastNode sink , boolean sinkIsSigned , int bitSize
210
- ) {
232
+ additional predicate isSink2 ( DataFlow:: TypeCastNode sink , FlowState state ) {
211
233
sink .asExpr ( ) instanceof ConversionExpr and
212
234
exists ( IntegerType integerType | sink .getResultType ( ) .getUnderlyingType ( ) = integerType |
213
235
(
214
- bitSize = integerType .getSize ( )
236
+ state . getBitSize ( ) = integerType .getSize ( )
215
237
or
216
238
not exists ( integerType .getSize ( ) ) and
217
- bitSize = getIntTypeBitSize ( sink .getFile ( ) )
239
+ state . getBitSize ( ) = getIntTypeBitSize ( sink .getFile ( ) , 0 )
218
240
) and
219
- if integerType instanceof SignedIntegerType then sinkIsSigned = true else sinkIsSigned = false
241
+ if integerType instanceof SignedIntegerType
242
+ then state .getIsSigned ( ) = true
243
+ else state .getIsSigned ( ) = false
220
244
) and
221
245
not exists ( ShrExpr shrExpr |
222
246
shrExpr .getLeftOperand ( ) .getGlobalValueNumber ( ) =
@@ -231,28 +255,20 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
231
255
// can sanitize the result of the conversion to prevent flow on to further sinks
232
256
// without needing to use `isSanitizerOut`, which doesn't work with flow states
233
257
// (and therefore the legacy `TaintTracking::Configuration` class).
234
- isSinkWithBitSize ( sink .getASuccessor ( ) , getSinkIsSigned ( state ) , getSinkBitSize ( state ) )
258
+ isSink2 ( sink .getASuccessor ( ) , state )
235
259
}
236
260
237
261
predicate isBarrier ( DataFlow:: Node node , FlowState state ) {
238
- exists ( boolean sinkIsSigned , int sinkBitSize |
239
- sinkIsSigned = getSinkIsSigned ( state ) and
240
- sinkBitSize = getSinkBitSize ( state )
262
+ // To catch flows that only happen on 32-bit architectures we consider an
263
+ // architecture-dependent sink bit size to be 32.
264
+ exists ( UpperBoundCheckGuard g , int effectiveSinkBitSize |
265
+ effectiveSinkBitSize = replaceZeroWith ( state .getBitSize ( ) , 32 )
241
266
|
242
- // To catch flows that only happen on 32-bit architectures we
243
- // consider an architecture-dependent sink bit size to be 32.
244
- exists ( UpperBoundCheckGuard g , int effectiveSinkBitSize |
245
- if sinkBitSize != 0 then effectiveSinkBitSize = sinkBitSize else effectiveSinkBitSize = 32
246
- |
247
- node = DataFlow:: BarrierGuard< upperBoundCheckGuard / 3 > :: getABarrierNodeForGuard ( g ) and
248
- g .isBoundFor ( effectiveSinkBitSize , sinkIsSigned )
249
- )
250
- or
251
- exists ( int bitSize |
252
- isIncorrectIntegerConversion ( getSourceBitSize ( state ) , bitSize ) and
253
- isSinkWithBitSize ( node , sinkIsSigned , bitSize )
254
- )
267
+ node = DataFlow:: BarrierGuard< upperBoundCheckGuard / 3 > :: getABarrierNodeForGuard ( g ) and
268
+ g .isBoundFor ( effectiveSinkBitSize , state .getIsSigned ( ) )
255
269
)
270
+ or
271
+ isSink2 ( node , state )
256
272
}
257
273
}
258
274
@@ -324,3 +340,8 @@ string describeBitSize(int bitSize, int intTypeBitSize) {
324
340
"a number with architecture-dependent bit-width, which is constrained to be " +
325
341
intTypeBitSize + "-bit by build constraints,"
326
342
}
343
+
344
+ bindingset [ inputBitSize, replacementForZero]
345
+ private int replaceZeroWith ( int inputBitSize , int replacementForZero ) {
346
+ if inputBitSize = 0 then result = replacementForZero else result = inputBitSize
347
+ }
0 commit comments