@@ -15,6 +15,11 @@ import semmle.code.cpp.dataflow.DataFlow
1515import semmle.code.cpp.valuenumbering.GlobalValueNumbering
1616
1717module 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+ */
1823 bindingset [ name, result ]
1924 string getNameOrInternalName ( string name ) {
2025 result = name or
@@ -288,6 +293,10 @@ module OOB {
288293 p .getType ( ) .getUnspecifiedType ( ) .( DerivedType ) .getBaseType ( ) .getSize ( ) .maximum ( 1 ) = result
289294 }
290295
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+ */
291300 predicate getANullTerminatedParameterIndex ( int i ) {
292301 // by default, require null-terminated parameters for src but
293302 // only if the type of src is a plain char pointer or wchar_t.
@@ -301,19 +310,28 @@ module OOB {
301310 )
302311 }
303312
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+ */
304317 predicate getASizeMultParameterIndex ( int i ) {
305318 // by default, there is no size multiplier parameter
306319 // exceptions: fread, fwrite, bsearch, qsort
307320 none ( )
308321 }
309322
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+ */
310327 predicate getALengthParameterIndex ( int i ) {
311328 // by default, size parameters do not exclude the size of a null terminator
312329 none ( )
313330 }
314331
315332 /**
316333 * 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.
317335 */
318336 predicate getAPermissiblyNullParameterIndex ( int i ) {
319337 // by default, pointer parameters are not allowed to be null
@@ -569,6 +587,8 @@ module OOB {
569587 }
570588
571589 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.
572592 exists ( int i |
573593 this .getTarget ( ) .( BufferAccessLibraryFunction ) .getASizeMultParameterIndex ( i ) and
574594 result = this .getArgument ( i ) .getValue ( ) .toInt ( )
@@ -596,6 +616,12 @@ module OOB {
596616 * Because range-analysis can over-widen bounds, take the minimum of range analysis and data-flow sources.
597617 *
598618 * 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+ * ```
599625 */
600626 private int getMaxStatedValue ( Expr e ) {
601627 result = upperBound ( e ) .minimum ( max ( getSourceConstantExpr ( e ) .getValue ( ) .toInt ( ) ) )
@@ -606,6 +632,12 @@ module OOB {
606632 * Because range-analysis can over-widen bounds, take the minimum of range analysis and data-flow sources.
607633 *
608634 * 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+ * ```
609641 */
610642 private int getMinStatedValue ( Expr e ) {
611643 result = upperBound ( e ) .minimum ( min ( getSourceConstantExpr ( e ) .getValue ( ) .toInt ( ) ) )
@@ -809,6 +841,8 @@ module OOB {
809841 override predicate isNotNullTerminated ( ) {
810842 // StringLiteral::getOriginalLength uses Expr::getValue, which implicitly truncates string literal
811843 // 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
812846 exists ( CharArrayInitializedWithStringLiteral init |
813847 init = this .( VariableAccess ) .getTarget ( ) .getInitializer ( ) .getExpr ( ) and
814848 init .getStringLiteralLength ( ) + 1 > init .getContainerLength ( )
@@ -869,7 +903,8 @@ module OOB {
869903 override predicate isNotNullTerminated ( ) { none ( ) }
870904 }
871905
872- private class PointerToObjectSourceOrSizeToBufferAccessFunctionConfig extends DataFlow:: Configuration {
906+ private class PointerToObjectSourceOrSizeToBufferAccessFunctionConfig extends DataFlow:: Configuration
907+ {
873908 PointerToObjectSourceOrSizeToBufferAccessFunctionConfig ( ) {
874909 this = "PointerToObjectSourceOrSizeToBufferAccessFunctionConfig"
875910 }
@@ -922,9 +957,11 @@ module OOB {
922957 )
923958 }
924959
925- private predicate bufferUseComputableBufferSize ( Expr bufferUse , Expr source , int size ) {
960+ private predicate bufferUseComputableBufferSize (
961+ Expr bufferUse , PointerToObjectSource source , int size
962+ ) {
926963 // flow from a PointerToObjectSource for which we can compute the exact size
927- size = source .( PointerToObjectSource ) . getFixedSize ( ) and
964+ size = source .getFixedSize ( ) and
928965 hasFlowFromBufferOrSizeExprToUse ( source , bufferUse )
929966 }
930967
@@ -933,12 +970,21 @@ module OOB {
933970 hasFlowFromBufferOrSizeExprToUse ( source .( DynamicAllocationSource ) , bufferUse )
934971 }
935972
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+ */
936978 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.
938980 size = getMinStatedValue ( sizeExpr ) and
939981 source = sizeExpr
940982 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);
942988 size = source .( DynamicAllocationSource ) .getFixedSize ( ) + getArithmeticOffsetValue ( sizeExpr , _) and
943989 hasFlowFromBufferOrSizeExprToUse ( source .( DynamicAllocationSource ) .getSizeExprSource ( _, _) ,
944990 sizeExpr )
@@ -1025,6 +1071,13 @@ module OOB {
10251071 )
10261072 }
10271073
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+
10281081 /**
10291082 * Holds if the BufferAccess `bufferAccess` results in a buffer overflow due to a size argument
10301083 * or buffer access offset being greater in size than the buffer size being accessed or written to.
@@ -1040,8 +1093,18 @@ module OOB {
10401093 ( bufferArg instanceof StaticBufferAccessSource implies bufferSource = bufferArg ) and
10411094 sizeExprComputableSize ( sizeArg , _, sizeArgValue ) and
10421095 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
10451108 computedBufferSize < computedSizeAccessed
10461109 )
10471110 }
@@ -1166,10 +1229,20 @@ module OOB {
11661229 bufferUseComputableBufferSize ( bufferArg , bufferSource , _) or
11671230 bufferUseNonComputableSize ( bufferArg , bufferSource )
11681231 ) 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+ )
11731246 )
11741247 }
11751248
0 commit comments