@@ -15,34 +15,99 @@ import cpp
1515import semmle.code.cpp.security.Overflow
1616import semmle.code.cpp.security.Security
1717import semmle.code.cpp.security.TaintTracking
18+ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
1819import TaintedWithPath
1920
20- predicate isRandCall ( FunctionCall fc ) { fc .getTarget ( ) .getName ( ) = "rand" }
21+ predicate isUnboundedRandCall ( FunctionCall fc ) {
22+ fc .getTarget ( ) .getName ( ) = "rand" and not bounded ( fc )
23+ }
24+
25+ /**
26+ * An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or
27+ * a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division.
28+ */
29+ pragma [ inline]
30+ predicate boundedDiv ( Expr e , Expr left ) { e = left }
31+
32+ /**
33+ * An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or
34+ * an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded
35+ * when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer
36+ * allowed by the result type of `rem`.
37+ */
38+ pragma [ inline]
39+ predicate boundedRem ( Expr e , Expr rem , Expr left , Expr right ) {
40+ e = left and
41+ upperBound ( right .getFullyConverted ( ) ) < exprMaxVal ( rem .getFullyConverted ( ) )
42+ }
2143
22- predicate isRandCallOrParent ( Expr e ) {
23- isRandCall ( e ) or
24- isRandCallOrParent ( e .getAChild ( ) )
44+ /**
45+ * An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr`
46+ * or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper
47+ * bounded by some number that is less than the maximum integer allowed by the result type of `andExpr`.
48+ */
49+ pragma [ inline]
50+ predicate boundedBitwiseAnd ( Expr e , Expr andExpr , Expr operand1 , Expr operand2 ) {
51+ operand1 != operand2 and
52+ e = operand1 and
53+ upperBound ( operand2 .getFullyConverted ( ) ) < exprMaxVal ( andExpr .getFullyConverted ( ) )
2554}
2655
27- predicate isRandValue ( Expr e ) {
28- isRandCall ( e )
56+ /**
57+ * Holds if `fc` is a part of the left operand of a binary operation that greatly reduces the range
58+ * of possible values.
59+ */
60+ predicate bounded ( Expr e ) {
61+ // For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the
62+ // maximum possible value of the result type of the operation.
63+ // For example, the function call `rand()` is considered bounded in the following program:
64+ // ```
65+ // int i = rand() % (UINT8_MAX + 1);
66+ // ```
67+ // but not in:
68+ // ```
69+ // unsigned char uc = rand() % (UINT8_MAX + 1);
70+ // ```
71+ exists ( RemExpr rem | boundedRem ( e , rem , rem .getLeftOperand ( ) , rem .getRightOperand ( ) ) )
72+ or
73+ exists ( AssignRemExpr rem | boundedRem ( e , rem , rem .getLValue ( ) , rem .getRValue ( ) ) )
74+ or
75+ exists ( BitwiseAndExpr andExpr |
76+ boundedBitwiseAnd ( e , andExpr , andExpr .getAnOperand ( ) , andExpr .getAnOperand ( ) )
77+ )
78+ or
79+ exists ( AssignAndExpr andExpr |
80+ boundedBitwiseAnd ( e , andExpr , andExpr .getAnOperand ( ) , andExpr .getAnOperand ( ) )
81+ )
82+ or
83+ // Optimitically assume that a division always yields a much smaller value.
84+ boundedDiv ( e , any ( DivExpr div ) .getLeftOperand ( ) )
85+ or
86+ boundedDiv ( e , any ( AssignDivExpr div ) .getLValue ( ) )
87+ }
88+
89+ predicate isUnboundedRandCallOrParent ( Expr e ) {
90+ isUnboundedRandCall ( e )
91+ or
92+ isUnboundedRandCallOrParent ( e .getAChild ( ) )
93+ }
94+
95+ predicate isUnboundedRandValue ( Expr e ) {
96+ isUnboundedRandCall ( e )
2997 or
3098 exists ( MacroInvocation mi |
3199 e = mi .getExpr ( ) and
32- isRandCallOrParent ( e )
100+ isUnboundedRandCallOrParent ( e )
33101 )
34102}
35103
36104class SecurityOptionsArith extends SecurityOptions {
37105 override predicate isUserInput ( Expr expr , string cause ) {
38- isRandValue ( expr ) and
39- cause = "rand" and
40- not expr .getParent * ( ) instanceof DivExpr
106+ isUnboundedRandValue ( expr ) and
107+ cause = "rand"
41108 }
42109}
43110
44- predicate isDiv ( VariableAccess va ) { exists ( AssignDivExpr div | div .getLValue ( ) = va ) }
45-
46111predicate missingGuard ( VariableAccess va , string effect ) {
47112 exists ( Operation op | op .getAnOperand ( ) = va |
48113 missingGuardAgainstUnderflow ( op , va ) and effect = "underflow"
@@ -52,29 +117,15 @@ predicate missingGuard(VariableAccess va, string effect) {
52117}
53118
54119class Configuration extends TaintTrackingConfiguration {
55- override predicate isSink ( Element e ) {
56- isDiv ( e )
57- or
58- missingGuard ( e , _)
59- }
60- }
120+ override predicate isSink ( Element e ) { missingGuard ( e , _) }
61121
62- /**
63- * A value that undergoes division is likely to be bounded within a safe
64- * range.
65- */
66- predicate guardedByAssignDiv ( Expr origin ) {
67- exists ( VariableAccess va |
68- taintedWithPath ( origin , va , _, _) and
69- isDiv ( va )
70- )
122+ override predicate isBarrier ( Expr e ) { super .isBarrier ( e ) or bounded ( e ) }
71123}
72124
73125from Expr origin , VariableAccess va , string effect , PathNode sourceNode , PathNode sinkNode
74126where
75127 taintedWithPath ( origin , va , sourceNode , sinkNode ) and
76- missingGuard ( va , effect ) and
77- not guardedByAssignDiv ( origin )
128+ missingGuard ( va , effect )
78129select va , sourceNode , sinkNode ,
79130 "$@ flows to here and is used in arithmetic, potentially causing an " + effect + "." , origin ,
80131 "Uncontrolled value"
0 commit comments