-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathDecOverflowWhenComparing.ql
More file actions
146 lines (135 loc) · 4.44 KB
/
DecOverflowWhenComparing.ql
File metadata and controls
146 lines (135 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* @name Decrementation overflow when comparing
* @id tob/cpp/dec-overflow-when-comparing
* @description This query finds unsigned integer overflows resulting from unchecked decrementation during comparison.
* @kind problem
* @tags security
* @problem.severity error
* @precision high
* @security-severity 5.0
* @group security
*/
import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.controlflow.StackVariableReachability
/**
* Holds if `node` overwrites `var` (assignment or declaration with initializer).
*/
predicate isDefOf(ControlFlowNode node, Variable var) {
node = var.getAnAccess() and node.(VariableAccess).isLValue()
or
node.(DeclStmt).getADeclaration() = var and exists(var.getInitializer())
or
node.(Assignment).getLValue().(VariableAccess).getTarget() = var
}
/**
* Identifies an unsigned postfix decrement inside a comparison against zero.
*/
pragma[nomagic]
predicate isDecInComparison(
PostfixDecrExpr dec, VariableAccess varAcc,
ComparisonOperation cmp, Variable var
) {
varAcc = var.getAnAccess() and
dec.getOperand() = varAcc.getExplicitlyConverted() and
varAcc.getUnderlyingType().(IntegralType).isUnsigned() and
cmp.getAnOperand().getAChild*() = varAcc and
cmp.getAnOperand() instanceof Zero and
lowerBound(varAcc) = 0
}
/**
* Identifies a non-assignment read of a variable
* (i.e., a use that could observe an overflowed value).
*/
pragma[nomagic]
predicate isReadOf(VariableAccess va, Variable var) {
va = var.getAnAccess() and
not exists(AssignExpr ae | ae.getLValue() = va)
}
/**
* Basic-block-level reachability from a decrement comparison to a use
* of the same stack variable, blocked by any definition of the variable.
*/
class DecOverflowReach extends StackVariableReachability {
DecOverflowReach() { this = "DecOverflowReach" }
override predicate isSource(ControlFlowNode node, StackVariable v) {
isDecInComparison(_, _, node, v)
}
override predicate isSink(ControlFlowNode node, StackVariable v) {
isReadOf(node, v)
}
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
isDefOf(node, v)
}
}
/**
* BB-level reachability for non-stack variables (globals, static locals).
* Holds if `sink` is reachable from the entry of `bb` without crossing
* a definition of `var`.
*/
private predicate nonStackBBEntryReaches(
BasicBlock bb, Variable var, ControlFlowNode sink
) {
exists(int n |
sink = bb.getNode(n) and
isReadOf(sink, var) and
not exists(int m | m < n | isDefOf(bb.getNode(m), var))
)
or
not isDefOf(bb.getNode(_), var) and
nonStackBBEntryReaches(bb.getASuccessor(), var, sink)
}
/**
* BB-level reachability from `source` to `sink` for non-stack variables,
* without crossing a definition of `var`.
*/
pragma[nomagic]
predicate nonStackReaches(
ComparisonOperation source, Variable var, ControlFlowNode sink
) {
not var instanceof StackVariable and
exists(BasicBlock bb, int i |
bb.getNode(i) = source and
not bb.isUnreachable()
|
exists(int j |
j > i and
sink = bb.getNode(j) and
isReadOf(sink, var) and
not exists(int k | k in [i + 1 .. j - 1] | isDefOf(bb.getNode(k), var))
)
or
not exists(int k | k > i | isDefOf(bb.getNode(k), var)) and
nonStackBBEntryReaches(bb.getASuccessor(), var, sink)
)
}
from
Variable var, VariableAccess varAcc, PostfixDecrExpr dec,
VariableAccess varAccAfterOverflow, ComparisonOperation cmp
where
isDecInComparison(dec, varAcc, cmp, var) and
isReadOf(varAccAfterOverflow, var) and
varAcc != varAccAfterOverflow and
// reachable without intervening overwrite
(
any(DecOverflowReach r).reaches(cmp, var, varAccAfterOverflow)
or
nonStackReaches(cmp, var, varAccAfterOverflow)
) and
// exclude accesses that are part of the comparison expression itself
not cmp.getAnOperand().getAChild*() = varAccAfterOverflow and
// var-- > 0 (0 < var--) then only accesses in false branch matter
(
if
(
cmp instanceof GTExpr and cmp.getRightOperand() instanceof Zero
or
cmp instanceof LTExpr and cmp.getLeftOperand() instanceof Zero
)
then cmp.getAFalseSuccessor().getASuccessor*() = varAccAfterOverflow
else any()
) and
// skip vendor code
not dec.getFile().getAbsolutePath().toLowerCase().matches(["%vendor%", "%third_party%"])
select dec, "Unsigned decrementation in comparison ($@) - $@", cmp, cmp.toString(),
varAccAfterOverflow, varAccAfterOverflow.toString()