@@ -153,13 +153,13 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
153
153
154
154
private int validBitSize ( ) { result = [ 7 , 8 , 15 , 16 , 31 , 32 , 63 , 64 ] }
155
155
156
- private newtype TarchitectureBitSize =
156
+ private newtype TArchitectureBitSize =
157
157
TMk32Bit ( ) or
158
158
TMk64Bit ( ) or
159
159
TMkArchitectureIndependent ( )
160
160
161
161
private newtype TMaxValueState =
162
- TMkMaxValueState ( int bitSize , TarchitectureBitSize architectureBitSize ) {
162
+ TMkMaxValueState ( int bitSize , TArchitectureBitSize architectureBitSize ) {
163
163
bitSize = validBitSize ( )
164
164
}
165
165
@@ -168,6 +168,11 @@ private class MaxValueState extends TMaxValueState {
168
168
/**
169
169
* Gets the smallest bitsize where the maximum value that could get to this
170
170
* point fits into an integer type whose maximum value is 2^(result) - 1.
171
+ *
172
+ * For example, if we know `1 << 12` can get to a particular program point,
173
+ * then the result would be 15, since a 16-bit signed integer can represent
174
+ * that value and that type has maximum value 2^15 -1. An unsigned 8-bit
175
+ * integer cannot represent that value as its maximum value is 2^8 - 1.
171
176
*/
172
177
int getBitSize ( ) { this = TMkMaxValueState ( result , _) }
173
178
@@ -185,7 +190,8 @@ private class MaxValueState extends TMaxValueState {
185
190
* Gets the bitsize we should use for a sink.
186
191
*
187
192
* If the architecture bit size is known, then we should use that. Otherwise,
188
- * we should use 32 bits, because that will lead to more results.
193
+ * we should use 32 bits, because that will find results that only exist on
194
+ * 32-bit architectures.
189
195
*/
190
196
bindingset [ default]
191
197
int getSinkBitSize ( int default ) {
@@ -243,6 +249,10 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
243
249
* Holds if the upper bound check ensures the non-constant operand is less
244
250
* than or equal to the maximum value for `bitSize` and `isSigned`. In this
245
251
* case, the upper bound check is a barrier guard.
252
+ *
253
+ * Note that we have to use floats here because integers in CodeQL are
254
+ * represented by 32-bit signed integers, which cannot represent some of the
255
+ * integer values which we will encounter.
246
256
*/
247
257
deprecated predicate isBoundFor ( int bitSize , boolean isSigned ) {
248
258
bitSize = [ 8 , 16 , 32 ] and
@@ -271,7 +281,12 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
271
281
/**
272
282
* Holds if this upper bound check ensures the non-constant operand is less
273
283
* than or equal to `2^(bitsize) - 1`. In this case, the upper bound check
274
- * is a barrier guard.
284
+ * is a barrier guard. `architectureBitSize` is used if the constant operand
285
+ * is `math.MaxInt` or `math.MaxUint`.
286
+ *
287
+ * Note that we have to use floats here because integers in CodeQL are
288
+ * represented by 32-bit signed integers, which cannot represent some of the
289
+ * integer values which we will encounter.
275
290
*/
276
291
predicate isBoundFor2 ( int bitSize , int architectureBitSize ) {
277
292
bitSize = validBitSize ( ) and
@@ -311,6 +326,28 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
311
326
* When this guarantees that a variable in the non-constant operand is less
312
327
* than some value this may be a barrier guard which should block some flow
313
328
* states and transform some others as they flow through.
329
+ *
330
+ * For example, in the following code:
331
+ * ```go
332
+ * if parsed <= math.MaxInt16 {
333
+ * _ = uint16(parsed)
334
+ * }
335
+ * ```
336
+ * `parsed < math.MaxInt16` is an `UpperBoundCheckGuard` and `uint16(parsed)`
337
+ * is an `UpperBoundCheck` that would be a barrier for flow states with bit
338
+ * size greater than 15 and would transform them to a flow state with bit size
339
+ * 15 and the same architecture bit size.
340
+ *
341
+ * However, in the following code:
342
+ * ```go
343
+ * parsed, _ := strconv.ParseUint(input, 10, 32)
344
+ * if parsed < 5 {
345
+ * _ = uint16(parsed)
346
+ * }
347
+ * ```
348
+ * `parsed < 5` is an `UpperBoundCheckGuard` and `uint16(parsed)` is a barrier
349
+ * for all flow states and would not transform any flow states, thus
350
+ * effectively blocking them.
314
351
*/
315
352
class UpperBoundCheck extends BarrierFlowStateTransformer {
316
353
UpperBoundCheckGuard g ;
@@ -320,6 +357,8 @@ class UpperBoundCheck extends BarrierFlowStateTransformer {
320
357
}
321
358
322
359
override predicate barrierFor ( MaxValueState flowstate ) {
360
+ // Use a default value of 32 for `MaxValueState.getSinkBitSize` because
361
+ // this will find results that only exist on 32-bit architectures.
323
362
g .isBoundFor2 ( flowstate .getBitSize ( ) , flowstate .getSinkBitSize ( 32 ) )
324
363
}
325
364
@@ -329,9 +368,9 @@ class UpperBoundCheck extends BarrierFlowStateTransformer {
329
368
max ( int bitsize |
330
369
bitsize = validBitSize ( ) and
331
370
bitsize < state .getBitSize ( ) and
371
+ // Use a default value of 32 for `MaxValueState.getSinkBitSize` because
372
+ // this will find results that only exist on 32-bit architectures.
332
373
not g .isBoundFor2 ( bitsize , state .getSinkBitSize ( 32 ) )
333
- |
334
- bitsize
335
374
) and
336
375
if exists ( state .getArchitectureBitSize ( ) )
337
376
then result .getArchitectureBitSize ( ) = state .getArchitectureBitSize ( )
@@ -341,10 +380,10 @@ class UpperBoundCheck extends BarrierFlowStateTransformer {
341
380
342
381
/**
343
382
* Holds if `source` is the result of a call to `strconv.Atoi`,
344
- * `strconv.ParseInt`, or `strconv.ParseUint`, `bitSize` is the bit size of
345
- * the smallest integer type which the result could be converted to without
346
- * data loss , and `isSigned` is true if the result is parsed as a signed
347
- * integer .
383
+ * `strconv.ParseInt`, or `strconv.ParseUint`, `bitSize` is the `bitSize`
384
+ * argument to that call (or 0 for `strconv.Atoi`) and hence must be between 0
385
+ * and 64 , and `isSigned` is true for `strconv.Atoi`, true for
386
+ * `strconv.ParseInt` and false for `strconv.ParseUint` .
348
387
*/
349
388
predicate isSourceWithBitSize ( DataFlow:: Node source , int bitSize , boolean isSigned ) {
350
389
exists ( DataFlow:: CallNode c , IntegerParser:: Range ip , int apparentBitSize |
@@ -362,9 +401,8 @@ predicate isSourceWithBitSize(DataFlow::Node source, int bitSize, boolean isSign
362
401
else apparentBitSize = rawBitSize .getIntValue ( )
363
402
)
364
403
) and
365
- // Note that `getIntTypeBitSize` can return 0, so `effectiveBitSize`
366
- // can be 0. Also `effectiveBitSize` is not necessarily the bit-size
367
- // of an integer type - it can be any integer between 0 and 64.
404
+ // Note that `bitSize` is not necessarily the bit-size of an integer type.
405
+ // It can be any integer between 0 and 64.
368
406
bitSize = replaceZeroWith ( apparentBitSize , getIntTypeBitSize ( source .getFile ( ) , 0 ) ) and
369
407
isSigned = ip .isSigned ( )
370
408
)
@@ -387,9 +425,9 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
387
425
state .getBitSize ( ) =
388
426
min ( int bitsize |
389
427
bitsize = validBitSize ( ) and
428
+ // The `bitSizeForZero` argument will not be used because on this
429
+ // branch `effectiveBitSize != 0`.
390
430
adjustBitSize ( effectiveBitSize , sourceIsSigned , 64 ) <= bitsize
391
- |
392
- bitsize
393
431
)
394
432
)
395
433
)
@@ -404,6 +442,8 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
404
442
additional predicate isSink2 ( DataFlow:: TypeCastNode sink , FlowState state ) {
405
443
sink .asExpr ( ) instanceof ConversionExpr and
406
444
exists ( int architectureBitSize , IntegerType integerType , int sinkBitsize , boolean sinkIsSigned |
445
+ // Use a default value of 32 for `MaxValueState.getSinkBitSize` because
446
+ // this will find results that only exist on 32-bit architectures.
407
447
architectureBitSize = getIntTypeBitSize ( sink .getFile ( ) , state .getSinkBitSize ( 32 ) ) and
408
448
not ( state .getArchitectureBitSize ( ) = 32 and architectureBitSize = 64 ) and
409
449
sink .getResultType ( ) .getUnderlyingType ( ) = integerType and
@@ -438,9 +478,7 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
438
478
439
479
predicate isBarrier ( DataFlow:: Node node , FlowState state ) {
440
480
// Safely guarded by a barrier guard.
441
- exists ( BarrierFlowStateTransformer bfst | node = bfst and bfst .barrierFor ( state ) |
442
- not exists ( bfst .transform ( state ) ) or bfst .transform ( state ) != state
443
- )
481
+ exists ( BarrierFlowStateTransformer bfst | node = bfst and bfst .barrierFor ( state ) )
444
482
or
445
483
// When there is a flow from a source to a sink, do not allow the flow to
446
484
// continue to a further sink.
0 commit comments