Skip to content

Commit e71c518

Browse files
committed
use StackVariableReachability
1 parent db277e1 commit e71c518

1 file changed

Lines changed: 61 additions & 49 deletions

File tree

cpp/src/security/DecOverflowWhenComparing/DecOverflowWhenComparing.ql

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
*/
1212

1313
import cpp
14-
import semmle.code.cpp.ir.IR
1514
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
15+
import semmle.code.cpp.controlflow.StackVariableReachability
1616

1717
/**
1818
* Holds if `node` overwrites `var` (assignment or declaration with initializer).
1919
*/
20-
predicate isDefOf(ControlFlowNode node, Variable var) {
20+
predicate isDefOf(ControlFlowNode node, StackVariable var) {
2121
node = var.getAnAccess() and node.(VariableAccess).isLValue()
2222
or
2323
node.(DeclStmt).getADeclaration() = var and exists(var.getInitializer())
@@ -26,62 +26,74 @@ predicate isDefOf(ControlFlowNode node, Variable var) {
2626
}
2727

2828
/**
29-
* Find CFG paths from start to end that do not cross over a definition of var.
29+
* Identifies an unsigned postfix decrement inside a comparison against zero.
3030
*/
31-
predicate successorGuarded(ControlFlowNode start, ControlFlowNode end, Variable var) {
32-
start = end
33-
or
34-
exists(ControlFlowNode interm |
35-
start.getASuccessor() = interm and
36-
not isDefOf(interm, var) and
37-
(
38-
interm.getASuccessor() = end
39-
or
40-
successorGuarded(interm, end, var)
41-
)
42-
)
43-
}
44-
45-
46-
from Variable var, VariableAccess varAcc, PostfixDecrExpr dec,
47-
VariableAccess varAccAfterOverflow, ComparisonOperation cmp
48-
where
49-
// get unsigned variable that is decremented
31+
pragma[nomagic]
32+
predicate isDecInComparison(
33+
PostfixDecrExpr dec, VariableAccess varAcc,
34+
ComparisonOperation cmp, StackVariable var
35+
) {
5036
varAcc = var.getAnAccess() and
51-
varAccAfterOverflow = var.getAnAccess() and
52-
varAcc != varAccAfterOverflow and
5337
dec.getOperand() = varAcc.getExplicitlyConverted() and
5438
varAcc.getUnderlyingType().(IntegralType).isUnsigned() and
55-
56-
// only decrementations inside comparisons
5739
cmp.getAnOperand().getAChild*() = varAcc and
5840
cmp.getAnOperand() instanceof Zero and
41+
lowerBound(varAcc) = 0
42+
}
43+
44+
/**
45+
* Identifies a non-assignment read of a variable
46+
* (i.e., a use that could observe an overflowed value).
47+
*/
48+
pragma[nomagic]
49+
predicate isReadOf(VariableAccess va, StackVariable var) {
50+
va = var.getAnAccess() and
51+
not exists(AssignExpr ae | ae.getLValue() = va)
52+
}
5953

60-
// only if the variable is used (not just overwritten) after the comparison
61-
successorGuarded(cmp, varAccAfterOverflow, var) and
62-
cmp.getAnOperand().getAChild*() != varAccAfterOverflow and
63-
not exists(AssignExpr ae | ae.getLValue() = varAccAfterOverflow) and
54+
/**
55+
* Basic-block-level reachability from a decrement comparison to a use
56+
* of the same variable, blocked by any definition of the variable.
57+
*/
58+
class DecOverflowReach extends StackVariableReachability {
59+
DecOverflowReach() { this = "DecOverflowReach" }
6460

65-
// var-- > 0 (0 < var--) then accesses only in false branch
66-
// var-- >= 0 then accesses in all branches
67-
// var-- == 0 then accesses in all branches
68-
// var-- != 0 then accesses in all branches
69-
if (
70-
(cmp instanceof GTExpr and cmp.getRightOperand() instanceof Zero)
71-
or
72-
(cmp instanceof LTExpr and cmp.getLeftOperand() instanceof Zero)
73-
)
74-
then
75-
cmp.getAFalseSuccessor().getASuccessor*() = varAccAfterOverflow
76-
else
77-
any()
61+
override predicate isSource(ControlFlowNode node, StackVariable v) {
62+
isDecInComparison(_, _, node, v)
63+
}
7864

79-
and
65+
override predicate isSink(ControlFlowNode node, StackVariable v) {
66+
isReadOf(node, v)
67+
}
8068

81-
// only if var may possibly be zero during comparison
82-
lowerBound(varAcc) = 0
69+
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
70+
isDefOf(node, v)
71+
}
72+
}
8373

74+
from
75+
SemanticStackVariable var, VariableAccess varAcc, PostfixDecrExpr dec,
76+
VariableAccess varAccAfterOverflow, ComparisonOperation cmp
77+
where
78+
isDecInComparison(dec, varAcc, cmp, var) and
79+
isReadOf(varAccAfterOverflow, var) and
80+
varAcc != varAccAfterOverflow and
81+
// reachable without intervening overwrite (basic-block-level analysis)
82+
any(DecOverflowReach r).reaches(cmp, var, varAccAfterOverflow) and
83+
// exclude accesses that are part of the comparison expression itself
84+
not cmp.getAnOperand().getAChild*() = varAccAfterOverflow and
85+
// var-- > 0 (0 < var--) then only accesses in false branch matter
86+
(
87+
if
88+
(
89+
cmp instanceof GTExpr and cmp.getRightOperand() instanceof Zero
90+
or
91+
cmp instanceof LTExpr and cmp.getLeftOperand() instanceof Zero
92+
)
93+
then cmp.getAFalseSuccessor().getASuccessor*() = varAccAfterOverflow
94+
else any()
95+
) and
8496
// skip vendor code
85-
and not dec.getFile().getAbsolutePath().toLowerCase().matches(["%vendor%", "%third_party%"])
86-
87-
select dec, "Unsigned decrementation in comparison ($@) - $@", cmp, cmp.toString(), varAccAfterOverflow, varAccAfterOverflow.toString()
97+
not dec.getFile().getAbsolutePath().toLowerCase().matches(["%vendor%", "%third_party%"])
98+
select dec, "Unsigned decrementation in comparison ($@) - $@", cmp, cmp.toString(),
99+
varAccAfterOverflow, varAccAfterOverflow.toString()

0 commit comments

Comments
 (0)