Skip to content

Commit e587465

Browse files
committed
Merge branch 'main' into atorralba/promote-ognl-injection
2 parents 1815656 + 0c970b5 commit e587465

File tree

198 files changed

+13509
-3958
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

198 files changed

+13509
-3958
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm
2+
* The `cpp/tainted-arithmetic`, `cpp/arithmetic-with-extreme-values`, and `cpp/uncontrolled-arithmetic` queries now recognize more functions as returning the absolute value of their input. As a result, they produce fewer false positives.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm
2+
* The "Tainted allocation size" query (cpp/uncontrolled-allocation-size) has been improved to produce fewer false positives.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The query "Uncontrolled arithmetic" (`cpp/uncontrolled-arithmetic`) has been improved to produce fewer false positives.

cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,99 @@ import cpp
1515
import semmle.code.cpp.security.Overflow
1616
import semmle.code.cpp.security.Security
1717
import semmle.code.cpp.security.TaintTracking
18+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
1819
import 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

36104
class 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-
46111
predicate 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

54119
class 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

73125
from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode
74126
where
75127
taintedWithPath(origin, va, sourceNode, sinkNode) and
76-
missingGuard(va, effect) and
77-
not guardedByAssignDiv(origin)
128+
missingGuard(va, effect)
78129
select va, sourceNode, sinkNode,
79130
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
80131
"Uncontrolled value"

cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313

1414
import cpp
15+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
1516
import semmle.code.cpp.security.TaintTracking
1617
import TaintedWithPath
1718

@@ -27,6 +28,27 @@ predicate allocSink(Expr alloc, Expr tainted) {
2728

2829
class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration {
2930
override predicate isSink(Element tainted) { allocSink(_, tainted) }
31+
32+
override predicate isBarrier(Expr e) {
33+
super.isBarrier(e)
34+
or
35+
// There can be two separate reasons for `convertedExprMightOverflow` not holding:
36+
// 1. `e` really cannot overflow.
37+
// 2. `e` isn't analyzable.
38+
// If we didn't rule out case 2 we would place barriers on anything that isn't analyzable.
39+
(
40+
e instanceof UnaryArithmeticOperation or
41+
e instanceof BinaryArithmeticOperation or
42+
e instanceof AssignArithmeticOperation
43+
) and
44+
not convertedExprMightOverflow(e)
45+
or
46+
// Subtracting two pointers is either well-defined (and the result will likely be small), or
47+
// terribly undefined and dangerous. Here, we assume that the programmer has ensured that the
48+
// result is well-defined (i.e., the two pointers point to the same object), and thus the result
49+
// will likely be small.
50+
e = any(PointerDiffExpr diff).getAnOperand()
51+
}
3052
}
3153

3254
predicate taintedAllocSize(

cpp/ql/src/Summary/LinesOfCode.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments.
55
* @kind metric
66
* @tags summary
7+
* lines-of-code
78
*/
89

910
import cpp
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
while(intIndex > 2)
2+
{
3+
...
4+
intIndex--;
5+
...
6+
} // GOOD: correct cycle
7+
...
8+
while(intIndex > 2)
9+
{
10+
...
11+
int intIndex;
12+
intIndex--;
13+
...
14+
} // BAD: the variable used in the condition does not change.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Using variables with the same name is dangerous. However, such a situation inside the while loop can create an infinite loop exhausting resources. Requires the attention of developers.</p>
7+
8+
</overview>
9+
<recommendation>
10+
<p>We recommend not to use local variables inside a loop if their names are the same as the variables in the condition of this loop.</p>
11+
12+
</recommendation>
13+
<example>
14+
<p>The following example demonstrates an erroneous and corrected use of a local variable within a loop.</p>
15+
<sample src="DeclarationOfVariableWithUnnecessarilyWideScope.c" />
16+
17+
</example>
18+
<references>
19+
20+
<li>
21+
CERT C Coding Standard:
22+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL01-C.+Do+not+reuse+variable+names+in+subscopes">DCL01-C. Do not reuse variable names in subscopes</a>.
23+
</li>
24+
25+
</references>
26+
</qhelp>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @name Errors When Using Variable Declaration Inside Loop
3+
* @description Using variables with the same name is dangerous.
4+
* However, such a situation inside the while loop can create an infinite loop exhausting resources.
5+
* Requires the attention of developers.
6+
* @kind problem
7+
* @id cpp/errors-when-using-variable-declaration-inside-loop
8+
* @problem.severity warning
9+
* @precision medium
10+
* @tags correctness
11+
* security
12+
* external/cwe/cwe-1126
13+
*/
14+
15+
import cpp
16+
17+
/**
18+
* Errors when using a variable declaration inside a loop.
19+
*/
20+
class DangerousWhileLoop extends WhileStmt {
21+
Expr exp;
22+
Declaration dl;
23+
24+
DangerousWhileLoop() {
25+
this = dl.getParentScope().(BlockStmt).getParent*() and
26+
exp = this.getCondition().getAChild*() and
27+
not exp instanceof PointerFieldAccess and
28+
not exp instanceof ValueFieldAccess and
29+
exp.(VariableAccess).getTarget().getName() = dl.getName() and
30+
not exp.getParent*() instanceof FunctionCall
31+
}
32+
33+
Declaration getDeclaration() { result = dl }
34+
35+
/** Holds when there are changes to the variables involved in the condition. */
36+
predicate isUseThisVariable() {
37+
exists(Variable v |
38+
this.getCondition().getAChild*().(VariableAccess).getTarget() = v and
39+
(
40+
exists(Assignment aexp |
41+
this = aexp.getEnclosingStmt().getParentStmt*() and
42+
(
43+
aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v
44+
or
45+
aexp.getLValue().(VariableAccess).getTarget() = v
46+
)
47+
)
48+
or
49+
exists(CrementOperation crm |
50+
this = crm.getEnclosingStmt().getParentStmt*() and
51+
crm.getOperand().(VariableAccess).getTarget() = v
52+
)
53+
)
54+
)
55+
}
56+
}
57+
58+
from DangerousWhileLoop lp
59+
where not lp.isUseThisVariable()
60+
select lp.getDeclaration(), "A variable with this name is used in the $@ condition.", lp, "loop"

0 commit comments

Comments
 (0)