@@ -15,6 +15,11 @@ import semmle.code.cpp.dataflow.DataFlow
15
15
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
16
16
17
17
module OOB {
18
+ /**
19
+ * Holds if `result` is either `name` or a string matching a pattern such as
20
+ * `__builtin_*name*_chk` or similar. This predicate exists to model internal functions
21
+ * such as `__builtin___memcpy_chk` under a common `memcpy` name in the table.
22
+ */
18
23
bindingset [ name, result ]
19
24
string getNameOrInternalName ( string name ) {
20
25
result = name or
@@ -288,6 +293,10 @@ module OOB {
288
293
p .getType ( ) .getUnspecifiedType ( ) .( DerivedType ) .getBaseType ( ) .getSize ( ) .maximum ( 1 ) = result
289
294
}
290
295
296
+ /**
297
+ * Holds if `i` is the index of a parameter of this function that requires arguments to be null-terminated.
298
+ * This predicate should be overriden by extending classes to specify null-terminated parameters, if necessary.
299
+ */
291
300
predicate getANullTerminatedParameterIndex ( int i ) {
292
301
// by default, require null-terminated parameters for src but
293
302
// only if the type of src is a plain char pointer or wchar_t.
@@ -301,19 +310,28 @@ module OOB {
301
310
)
302
311
}
303
312
313
+ /**
314
+ * Holds if `i` is the index of a parameter of this function that is a size multiplier.
315
+ * This predicate should be overriden by extending classes to specify size multiplier parameters, if necessary.
316
+ */
304
317
predicate getASizeMultParameterIndex ( int i ) {
305
318
// by default, there is no size multiplier parameter
306
319
// exceptions: fread, fwrite, bsearch, qsort
307
320
none ( )
308
321
}
309
322
323
+ /**
324
+ * Holds if `i` is the index of a parameter of this function that expects an element count rather than buffer size argument.
325
+ * This predicate should be overriden by extending classes to specify length parameters, if necessary.
326
+ */
310
327
predicate getALengthParameterIndex ( int i ) {
311
328
// by default, size parameters do not exclude the size of a null terminator
312
329
none ( )
313
330
}
314
331
315
332
/**
316
333
* Holds if the read or write parameter at index `i` is allowed to be null.
334
+ * This predicate should be overriden by extending classes to specify permissibly null parameters, if necessary.
317
335
*/
318
336
predicate getAPermissiblyNullParameterIndex ( int i ) {
319
337
// by default, pointer parameters are not allowed to be null
@@ -569,6 +587,8 @@ module OOB {
569
587
}
570
588
571
589
int getSizeMultArgValue ( ) {
590
+ // Note: This predicate currently expects the size multiplier argument to be a constant.
591
+ // This implementation could be improved with range-analysis or data-flow to determine the argument value.
572
592
exists ( int i |
573
593
this .getTarget ( ) .( BufferAccessLibraryFunction ) .getASizeMultParameterIndex ( i ) and
574
594
result = this .getArgument ( i ) .getValue ( ) .toInt ( )
@@ -596,6 +616,12 @@ module OOB {
596
616
* Because range-analysis can over-widen bounds, take the minimum of range analysis and data-flow sources.
597
617
*
598
618
* If there is no source value that flows to `e`, this predicate does not hold.
619
+ *
620
+ * This predicate, if `e` is the size argument to malloc, would return `20` for the following example:
621
+ * ```
622
+ * size_t sz = condition ? 10 : 20;
623
+ * malloc(sz);
624
+ * ```
599
625
*/
600
626
private int getMaxStatedValue ( Expr e ) {
601
627
result = upperBound ( e ) .minimum ( max ( getSourceConstantExpr ( e ) .getValue ( ) .toInt ( ) ) )
@@ -606,6 +632,12 @@ module OOB {
606
632
* Because range-analysis can over-widen bounds, take the minimum of range analysis and data-flow sources.
607
633
*
608
634
* If there is no source value that flows to `e`, this predicate does not hold.
635
+ *
636
+ * This predicate, if `e` is the size argument to malloc, would return `10` for the following example:
637
+ * ```
638
+ * size_t sz = condition ? 10 : 20;
639
+ * malloc(sz);
640
+ * ```
609
641
*/
610
642
private int getMinStatedValue ( Expr e ) {
611
643
result = upperBound ( e ) .minimum ( min ( getSourceConstantExpr ( e ) .getValue ( ) .toInt ( ) ) )
@@ -809,6 +841,8 @@ module OOB {
809
841
override predicate isNotNullTerminated ( ) {
810
842
// StringLiteral::getOriginalLength uses Expr::getValue, which implicitly truncates string literal
811
843
// values to the length fitting the buffer they are assigned to, thus breaking the 'obvious' check.
844
+ // Note: `CharArrayInitializedWithStringLiteral` falsely reports the string literal length in certain cases
845
+ // (e.g. when the string literal contains escape characters or on certain compilers), resulting in false-negatives
812
846
exists ( CharArrayInitializedWithStringLiteral init |
813
847
init = this .( VariableAccess ) .getTarget ( ) .getInitializer ( ) .getExpr ( ) and
814
848
init .getStringLiteralLength ( ) + 1 > init .getContainerLength ( )
@@ -869,7 +903,8 @@ module OOB {
869
903
override predicate isNotNullTerminated ( ) { none ( ) }
870
904
}
871
905
872
- private class PointerToObjectSourceOrSizeToBufferAccessFunctionConfig extends DataFlow:: Configuration {
906
+ private class PointerToObjectSourceOrSizeToBufferAccessFunctionConfig extends DataFlow:: Configuration
907
+ {
873
908
PointerToObjectSourceOrSizeToBufferAccessFunctionConfig ( ) {
874
909
this = "PointerToObjectSourceOrSizeToBufferAccessFunctionConfig"
875
910
}
@@ -922,9 +957,11 @@ module OOB {
922
957
)
923
958
}
924
959
925
- private predicate bufferUseComputableBufferSize ( Expr bufferUse , Expr source , int size ) {
960
+ private predicate bufferUseComputableBufferSize (
961
+ Expr bufferUse , PointerToObjectSource source , int size
962
+ ) {
926
963
// flow from a PointerToObjectSource for which we can compute the exact size
927
- size = source .( PointerToObjectSource ) . getFixedSize ( ) and
964
+ size = source .getFixedSize ( ) and
928
965
hasFlowFromBufferOrSizeExprToUse ( source , bufferUse )
929
966
}
930
967
@@ -933,12 +970,21 @@ module OOB {
933
970
hasFlowFromBufferOrSizeExprToUse ( source .( DynamicAllocationSource ) , bufferUse )
934
971
}
935
972
973
+ /**
974
+ * Relates `sizeExpr`, a buffer access size expresion, to `source`, which is either `sizeExpr`
975
+ * if `sizeExpr` has a stated value, or a `DynamicAllocationSource::getSizeExprSource` for which
976
+ * we can compute the exact size and that has flow to `sizeExpr`.
977
+ */
936
978
private predicate sizeExprComputableSize ( Expr sizeExpr , Expr source , int size ) {
937
- // computable direct value
979
+ // computable direct value, e.g. array_base[10], where "10" is sizeExpr and source.
938
980
size = getMinStatedValue ( sizeExpr ) and
939
981
source = sizeExpr
940
982
or
941
- // computable source value that flows to the size expression
983
+ // computable source value that flows to the size expression, e.g. in cases such as the following:
984
+ // size_t sz = 10;
985
+ // malloc(sz);
986
+ // ... sz passed interprocedurally to another function ...
987
+ // use(p, sz + 1);
942
988
size = source .( DynamicAllocationSource ) .getFixedSize ( ) + getArithmeticOffsetValue ( sizeExpr , _) and
943
989
hasFlowFromBufferOrSizeExprToUse ( source .( DynamicAllocationSource ) .getSizeExprSource ( _, _) ,
944
990
sizeExpr )
@@ -1025,6 +1071,13 @@ module OOB {
1025
1071
)
1026
1072
}
1027
1073
1074
+ /**
1075
+ * Holds if `sizeArg` is the right operand of a `PointerSubExpr`
1076
+ */
1077
+ predicate isSizeArgPointerSubExprRightOperand ( Expr sizeArg ) {
1078
+ exists ( PointerSubExpr subExpr | sizeArg = subExpr .getRightOperand ( ) )
1079
+ }
1080
+
1028
1081
/**
1029
1082
* Holds if the BufferAccess `bufferAccess` results in a buffer overflow due to a size argument
1030
1083
* or buffer access offset being greater in size than the buffer size being accessed or written to.
@@ -1040,8 +1093,18 @@ module OOB {
1040
1093
( bufferArg instanceof StaticBufferAccessSource implies bufferSource = bufferArg ) and
1041
1094
sizeExprComputableSize ( sizeArg , _, sizeArgValue ) and
1042
1095
computedBufferSize = bufferArgSize - sizeMult .( float ) * getArithmeticOffsetValue ( bufferArg , _) and
1043
- computedSizeAccessed =
1044
- sizeMult .( float ) * ( sizeArgValue + argNumCharactersOffset ( bufferAccess , sizeArg ) ) .( float ) and
1096
+ // Handle cases such as *(ptr - 1)
1097
+ (
1098
+ if isSizeArgPointerSubExprRightOperand ( sizeArg )
1099
+ then
1100
+ computedSizeAccessed =
1101
+ sizeMult .( float ) *
1102
+ ( - sizeArgValue + argNumCharactersOffset ( bufferAccess , sizeArg ) ) .( float )
1103
+ else
1104
+ computedSizeAccessed =
1105
+ sizeMult .( float ) *
1106
+ ( sizeArgValue + argNumCharactersOffset ( bufferAccess , sizeArg ) ) .( float )
1107
+ ) and
1045
1108
computedBufferSize < computedSizeAccessed
1046
1109
)
1047
1110
}
@@ -1166,10 +1229,20 @@ module OOB {
1166
1229
bufferUseComputableBufferSize ( bufferArg , bufferSource , _) or
1167
1230
bufferUseNonComputableSize ( bufferArg , bufferSource )
1168
1231
) and
1169
- // Not a size expression for which we can compute a specific size
1170
- not sizeExprComputableSize ( sizeArg , _, _) and
1171
- // If the lower bound is less than zero, taking into account any offsets
1172
- lowerBound ( sizeArg ) + getArithmeticOffsetValue ( bufferArg , _) < 0
1232
+ (
1233
+ // Not a size expression for which we can compute a specific size
1234
+ // and with a lower bound that is less than zero, taking into account offsets
1235
+ not sizeExprComputableSize ( sizeArg , _, _) and
1236
+ lowerBound ( sizeArg ) + getArithmeticOffsetValue ( bufferArg , _) < 0
1237
+ or
1238
+ // A size expression for which we can compute a specific size and that size is less than zero
1239
+ sizeExprComputableSize ( sizeArg , _, _) and
1240
+ (
1241
+ if isSizeArgPointerSubExprRightOperand ( sizeArg )
1242
+ then - getMinStatedValue ( sizeArg ) + getArithmeticOffsetValue ( bufferArg , _) < 0
1243
+ else getMinStatedValue ( sizeArg ) + getArithmeticOffsetValue ( bufferArg , _) < 0
1244
+ )
1245
+ )
1173
1246
)
1174
1247
}
1175
1248
0 commit comments