Skip to content

Commit 530c950

Browse files
committed
C++: Fix formatting.
1 parent 66f11d4 commit 530c950

File tree

2 files changed

+166
-166
lines changed

2 files changed

+166
-166
lines changed
Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1-
<!DOCTYPE qhelp PUBLIC
2-
"-//Semmle//qhelp//EN"
3-
"qhelp.dtd">
4-
<qhelp>
5-
<overview>
6-
<p>The program performs an out-of-bounds read or write operation, which can cause program instability. In addition, attackers may take advantage of the situation, and implement techniques to use this vulnerability to execute arbitrary code.</p>
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>The program performs an out-of-bounds read or write operation, which can cause program instability. In addition, attackers may take advantage of the situation, and implement techniques to use this vulnerability to execute arbitrary code.</p>
77

8-
</overview>
9-
<recommendation>
8+
</overview>
9+
<recommendation>
1010

11-
<p>Ensure that pointer dereferences are properly guarded to ensure that they cannot be used to read or write past the end of the allocation.</p>
11+
<p>Ensure that pointer dereferences are properly guarded to ensure that they cannot be used to read or write past the end of the allocation.</p>
1212

13-
</recommendation>
14-
<example>
15-
<p>The first example allocates a buffer of size <code>size</code> and creates a local variable that stores the location that is one byte past the end of the allocation.
16-
This local variable is then dereferenced, which results in an out-of-bounds write.
17-
The second example subtracts one from the <code>end</code> variable before dereferencing it. This subtraction ensures that the write correctly updates the final byte of the allocation.</p>
18-
<sample src="InvalidPointerDeref.cpp" />
13+
</recommendation>
14+
<example>
15+
<p>The first example allocates a buffer of size <code>size</code> and creates a local variable that stores the location that is one byte past the end of the allocation.
16+
This local variable is then dereferenced, which results in an out-of-bounds write.
17+
The second example subtracts one from the <code>end</code> variable before dereferencing it. This subtraction ensures that the write correctly updates the final byte of the allocation.</p>
18+
<sample src="InvalidPointerDeref.cpp" />
1919

20-
</example>
21-
<references>
20+
</example>
21+
<references>
2222

23-
<li>CERT C Coding Standard:
24-
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.</li>
25-
<li>
26-
OWASP:
27-
<a href="https://owasp.org/www-community/vulnerabilities/Buffer_Overflow">Buffer Overflow</a>.
28-
</li>
23+
<li>CERT C Coding Standard:
24+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.</li>
25+
<li>
26+
OWASP:
27+
<a href="https://owasp.org/www-community/vulnerabilities/Buffer_Overflow">Buffer Overflow</a>.
28+
</li>
2929

30-
</references>
31-
</qhelp>
30+
</references>
31+
</qhelp>
Lines changed: 141 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,152 @@
1-
/**
2-
* @name Invalid pointer dereference
3-
* @description Dereferencing an out-of-bounds pointer is undefined behavior and may lead to security vulnerabilities.
4-
* @kind path-problem
5-
* @problem.severity error
6-
* @security-severity 9.3
7-
* @precision medium
8-
* @id cpp/invalid-pointer-deref
9-
* @tags reliability
10-
* security
11-
* experimental
12-
* external/cwe/cwe-119
13-
* external/cwe/cwe-125
14-
* external/cwe/cwe-193
15-
* external/cwe/cwe-787
16-
*/
1+
/**
2+
* @name Invalid pointer dereference
3+
* @description Dereferencing an out-of-bounds pointer is undefined behavior and may lead to security vulnerabilities.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @security-severity 9.3
7+
* @precision medium
8+
* @id cpp/invalid-pointer-deref
9+
* @tags reliability
10+
* security
11+
* experimental
12+
* external/cwe/cwe-119
13+
* external/cwe/cwe-125
14+
* external/cwe/cwe-193
15+
* external/cwe/cwe-787
16+
*/
1717

18-
/*
19-
* High-level description of the query:
20-
*
21-
* The goal of this query is to identify issues such as:
22-
* ```cpp
23-
* 1. int* base = new int[size];
24-
* 2. int* end = base + size;
25-
* 3. for(int* p = base; p <= end; ++p) {
26-
* 4. *p = 0; // BUG: Should have been bounded by `p < end`.
27-
* 5. }
28-
* ```
29-
* In order to do this, we split the problem into three subtasks:
30-
* 1. First, we find flow from `new int[size]` to `base + size`.
31-
* 2. Then, we find flow from `base + size` to `end` (on line 3).
32-
* 3. Finally, we use range-analysis to find a write to (or read from) a pointer that may be greater than or equal to `end`.
33-
*
34-
* Step 1 is implemented in `AllocationToInvalidPointer.qll`, and step 2 is implemented by
35-
* `InvalidPointerToDereference.qll`. See those files for the description of these.
36-
*
37-
* This file imports both libraries and defines a final dataflow configuration that constructs the full path from
38-
* the allocation to the dereference of the out-of-bounds pointer. This is done for several reasons:
39-
* 1. It means the user is able to inspect the entire path from the allocation to the dereference, which can be useful
40-
* to understand the problem highlighted.
41-
* 2. It ensures that the call-contexts line up correctly when we transition from step 1 to step 2. See the
42-
* `test_missing_call_context_1` and `test_missing_call_context_2` tests for how this may flag false positives
43-
* without this final configuration.
44-
*
45-
* The source of the final path is an allocation that is:
46-
* 1. identified as flowing to an invalid pointer (by `AllocationToInvalidPointer`), and
47-
* 2. for which the invalid pointer flows to a dereference (as identified by `InvalidPointerToDereference`).
48-
*
49-
* The path can be described in 3 "chunks":
50-
* 1. One path from the allocation to the construction of the invalid pointer
51-
* 2. Another path from the construction of the invalid pointer to the final pointer that is about to be dereferenced.
52-
* 3. Finally, a single step from the dataflow node that represents the final pointer to the dereference.
53-
*
54-
* Step 1 happens when the flow state is `TInitial`, and step 2 and 3 happen when the flow state is `TPointerArith(pai)`
55-
* where the pointer-arithmetic instruction `pai` tracks the instruction that generated the out-of-bounds pointer. This
56-
* instruction is used in the construction of the alert message.
57-
*
58-
* The set of pointer-arithmetic instructions that define the `TPointerArith` flow state is restricted to be the pointer-
59-
* arithmetic instructions that both receive flow from the allocation (as identified by `AllocationToInvalidPointer.qll`),
60-
* and further flow to a dereference (as identified by `InvalidPointerToDereference.qll`).
61-
*/
18+
/*
19+
* High-level description of the query:
20+
*
21+
* The goal of this query is to identify issues such as:
22+
* ```cpp
23+
* 1. int* base = new int[size];
24+
* 2. int* end = base + size;
25+
* 3. for(int* p = base; p <= end; ++p) {
26+
* 4. *p = 0; // BUG: Should have been bounded by `p < end`.
27+
* 5. }
28+
* ```
29+
* In order to do this, we split the problem into three subtasks:
30+
* 1. First, we find flow from `new int[size]` to `base + size`.
31+
* 2. Then, we find flow from `base + size` to `end` (on line 3).
32+
* 3. Finally, we use range-analysis to find a write to (or read from) a pointer that may be greater than or equal to `end`.
33+
*
34+
* Step 1 is implemented in `AllocationToInvalidPointer.qll`, and step 2 is implemented by
35+
* `InvalidPointerToDereference.qll`. See those files for the description of these.
36+
*
37+
* This file imports both libraries and defines a final dataflow configuration that constructs the full path from
38+
* the allocation to the dereference of the out-of-bounds pointer. This is done for several reasons:
39+
* 1. It means the user is able to inspect the entire path from the allocation to the dereference, which can be useful
40+
* to understand the problem highlighted.
41+
* 2. It ensures that the call-contexts line up correctly when we transition from step 1 to step 2. See the
42+
* `test_missing_call_context_1` and `test_missing_call_context_2` tests for how this may flag false positives
43+
* without this final configuration.
44+
*
45+
* The source of the final path is an allocation that is:
46+
* 1. identified as flowing to an invalid pointer (by `AllocationToInvalidPointer`), and
47+
* 2. for which the invalid pointer flows to a dereference (as identified by `InvalidPointerToDereference`).
48+
*
49+
* The path can be described in 3 "chunks":
50+
* 1. One path from the allocation to the construction of the invalid pointer
51+
* 2. Another path from the construction of the invalid pointer to the final pointer that is about to be dereferenced.
52+
* 3. Finally, a single step from the dataflow node that represents the final pointer to the dereference.
53+
*
54+
* Step 1 happens when the flow state is `TInitial`, and step 2 and 3 happen when the flow state is `TPointerArith(pai)`
55+
* where the pointer-arithmetic instruction `pai` tracks the instruction that generated the out-of-bounds pointer. This
56+
* instruction is used in the construction of the alert message.
57+
*
58+
* The set of pointer-arithmetic instructions that define the `TPointerArith` flow state is restricted to be the pointer-
59+
* arithmetic instructions that both receive flow from the allocation (as identified by `AllocationToInvalidPointer.qll`),
60+
* and further flow to a dereference (as identified by `InvalidPointerToDereference.qll`).
61+
*/
6262

63-
import cpp
64-
import semmle.code.cpp.dataflow.new.DataFlow
65-
import semmle.code.cpp.ir.IR
66-
import FinalFlow::PathGraph
67-
import semmle.code.cpp.security.InvalidPointerDereference.AllocationToInvalidPointer
68-
import semmle.code.cpp.security.InvalidPointerDereference.InvalidPointerToDereference
63+
import cpp
64+
import semmle.code.cpp.dataflow.new.DataFlow
65+
import semmle.code.cpp.ir.IR
66+
import FinalFlow::PathGraph
67+
import semmle.code.cpp.security.InvalidPointerDereference.AllocationToInvalidPointer
68+
import semmle.code.cpp.security.InvalidPointerDereference.InvalidPointerToDereference
6969

70-
/**
71-
* A configuration that represents the full dataflow path all the way from
72-
* the allocation to the dereference. We need this final dataflow traversal
73-
* to ensure that the transition from the sink in `AllocToInvalidPointerConfig`
74-
* to the source in `InvalidPointerToDerefFlow` did not make us construct an
75-
* infeasible path (which can happen since the transition from one configuration
76-
* to the next does not preserve information about call contexts).
77-
*/
78-
module FinalConfig implements DataFlow::StateConfigSig {
79-
newtype FlowState =
80-
additional TInitial() or
81-
additional TPointerArith(PointerArithmeticInstruction pai) {
82-
operationIsOffBy(_, pai, _, _, _, _, _)
83-
}
84-
85-
predicate isSource(DataFlow::Node source, FlowState state) {
86-
state = TInitial() and
87-
operationIsOffBy(source, _, _, _, _, _, _)
88-
}
89-
90-
predicate isSink(DataFlow::Node sink, FlowState state) {
91-
exists(PointerArithmeticInstruction pai |
92-
operationIsOffBy(_, pai, _, _, _, sink, _) and
93-
state = TPointerArith(pai)
94-
)
70+
/**
71+
* A configuration that represents the full dataflow path all the way from
72+
* the allocation to the dereference. We need this final dataflow traversal
73+
* to ensure that the transition from the sink in `AllocToInvalidPointerConfig`
74+
* to the source in `InvalidPointerToDerefFlow` did not make us construct an
75+
* infeasible path (which can happen since the transition from one configuration
76+
* to the next does not preserve information about call contexts).
77+
*/
78+
module FinalConfig implements DataFlow::StateConfigSig {
79+
newtype FlowState =
80+
additional TInitial() or
81+
additional TPointerArith(PointerArithmeticInstruction pai) {
82+
operationIsOffBy(_, pai, _, _, _, _, _)
9583
}
9684

97-
predicate isAdditionalFlowStep(
98-
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
99-
) {
100-
// A step from the left-hand side of a pointer-arithmetic operation that has been
101-
// identified as creating an out-of-bounds pointer to the result of the pointer-arithmetic
102-
// operation.
103-
exists(PointerArithmeticInstruction pai |
104-
pointerAddInstructionHasBounds(_, pai, node1, _) and
105-
operationIsOffBy(_, pai, node2, _, _, _, _) and
106-
state1 = TInitial() and
107-
state2 = TPointerArith(pai)
108-
)
109-
or
110-
// A step from an out-of-bounds address to the operation (which is either a `StoreInstruction`
111-
// or a `LoadInstruction`) that dereferences the address.
112-
// This step exists purely for aesthetic reasons: we want the alert to be placed at the operation
113-
// that causes the dereference, and not at the address that flows into the operation.
114-
state1 = state2 and
115-
exists(PointerArithmeticInstruction pai |
116-
state1 = TPointerArith(pai) and
117-
operationIsOffBy(_, pai, _, node1, _, node2, _)
118-
)
119-
}
85+
predicate isSource(DataFlow::Node source, FlowState state) {
86+
state = TInitial() and
87+
operationIsOffBy(source, _, _, _, _, _, _)
12088
}
12189

122-
module FinalFlow = DataFlow::GlobalWithState<FinalConfig>;
90+
predicate isSink(DataFlow::Node sink, FlowState state) {
91+
exists(PointerArithmeticInstruction pai |
92+
operationIsOffBy(_, pai, _, _, _, sink, _) and
93+
state = TPointerArith(pai)
94+
)
95+
}
12396

124-
/**
125-
* Holds if `source` is an allocation that flows into the left-hand side of `pai`, which produces an out-of-bounds
126-
* pointer that flows into an address that is dereferenced by `sink` (which is either a `LoadInstruction` or a
127-
* `StoreInstruction`). The end result is that `sink` writes to an address that is off-by-`delta` from the end of
128-
* the allocation. The string `operation` describes whether the `sink` is a load or a store (which is then used
129-
* to produce the alert message).
130-
*
131-
* Note that multiple `delta`s can exist for a given `(source, pai, sink)` triplet.
132-
*/
133-
predicate hasFlowPath(
134-
FinalFlow::PathNode source, FinalFlow::PathNode sink, PointerArithmeticInstruction pai,
135-
string operation, int delta
97+
predicate isAdditionalFlowStep(
98+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
13699
) {
137-
FinalFlow::flowPath(source, sink) and
138-
operationIsOffBy(source.getNode(), pai, _, _, operation, sink.getNode(), delta) and
139-
sink.getState() = FinalConfig::TPointerArith(pai)
100+
// A step from the left-hand side of a pointer-arithmetic operation that has been
101+
// identified as creating an out-of-bounds pointer to the result of the pointer-arithmetic
102+
// operation.
103+
exists(PointerArithmeticInstruction pai |
104+
pointerAddInstructionHasBounds(_, pai, node1, _) and
105+
operationIsOffBy(_, pai, node2, _, _, _, _) and
106+
state1 = TInitial() and
107+
state2 = TPointerArith(pai)
108+
)
109+
or
110+
// A step from an out-of-bounds address to the operation (which is either a `StoreInstruction`
111+
// or a `LoadInstruction`) that dereferences the address.
112+
// This step exists purely for aesthetic reasons: we want the alert to be placed at the operation
113+
// that causes the dereference, and not at the address that flows into the operation.
114+
state1 = state2 and
115+
exists(PointerArithmeticInstruction pai |
116+
state1 = TPointerArith(pai) and
117+
operationIsOffBy(_, pai, _, node1, _, node2, _)
118+
)
140119
}
120+
}
121+
122+
module FinalFlow = DataFlow::GlobalWithState<FinalConfig>;
123+
124+
/**
125+
* Holds if `source` is an allocation that flows into the left-hand side of `pai`, which produces an out-of-bounds
126+
* pointer that flows into an address that is dereferenced by `sink` (which is either a `LoadInstruction` or a
127+
* `StoreInstruction`). The end result is that `sink` writes to an address that is off-by-`delta` from the end of
128+
* the allocation. The string `operation` describes whether the `sink` is a load or a store (which is then used
129+
* to produce the alert message).
130+
*
131+
* Note that multiple `delta`s can exist for a given `(source, pai, sink)` triplet.
132+
*/
133+
predicate hasFlowPath(
134+
FinalFlow::PathNode source, FinalFlow::PathNode sink, PointerArithmeticInstruction pai,
135+
string operation, int delta
136+
) {
137+
FinalFlow::flowPath(source, sink) and
138+
operationIsOffBy(source.getNode(), pai, _, _, operation, sink.getNode(), delta) and
139+
sink.getState() = FinalConfig::TPointerArith(pai)
140+
}
141141

142-
from
143-
FinalFlow::PathNode source, FinalFlow::PathNode sink, int k, string kstr,
144-
PointerArithmeticInstruction pai, string operation, Expr offset, DataFlow::Node n
145-
where
146-
k = min(int cand | hasFlowPath(source, sink, pai, operation, cand)) and
147-
offset = pai.getRight().getUnconvertedResultExpression() and
148-
n = source.getNode() and
149-
if k = 0 then kstr = "" else kstr = " + " + k
150-
select sink.getNode(), source, sink,
151-
"This " + operation + " might be out of bounds, as the pointer might be equal to $@ + $@" + kstr +
152-
".", n, n.toString(), offset, offset.toString()
142+
from
143+
FinalFlow::PathNode source, FinalFlow::PathNode sink, int k, string kstr,
144+
PointerArithmeticInstruction pai, string operation, Expr offset, DataFlow::Node n
145+
where
146+
k = min(int cand | hasFlowPath(source, sink, pai, operation, cand)) and
147+
offset = pai.getRight().getUnconvertedResultExpression() and
148+
n = source.getNode() and
149+
if k = 0 then kstr = "" else kstr = " + " + k
150+
select sink.getNode(), source, sink,
151+
"This " + operation + " might be out of bounds, as the pointer might be equal to $@ + $@" + kstr +
152+
".", n, n.toString(), offset, offset.toString()

0 commit comments

Comments
 (0)