Skip to content

Commit 6d22e35

Browse files
authored
Merge pull request #2151 from raulgarciamsft/users/raul/oss
Users/raul/oss
2 parents 73c677d + 4ac32c4 commit 6d22e35

File tree

81 files changed

+3458
-32
lines changed

Some content is hidden

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

81 files changed

+3458
-32
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import cpp
2+
import semmle.code.cpp.dataflow.TaintTracking
3+
private import semmle.code.cpp.dataflow.RecursionPrevention
4+
5+
/**
6+
* A buffer which includes an allocation size.
7+
*/
8+
abstract class BufferWithSize extends DataFlow::Node {
9+
abstract Expr getSizeExpr();
10+
11+
BufferAccess getAnAccess() {
12+
any(BufferWithSizeConfig bsc).hasFlow(this, DataFlow::exprNode(result.getPointer()))
13+
}
14+
}
15+
16+
/** An allocation function. */
17+
abstract class Alloc extends Function { }
18+
19+
/**
20+
* Allocation functions identified by the QL for C/C++ standard library.
21+
*/
22+
class DefaultAlloc extends Alloc {
23+
DefaultAlloc() { allocationFunction(this) }
24+
}
25+
26+
/** A buffer created through a call to an allocation function. */
27+
class AllocBuffer extends BufferWithSize {
28+
FunctionCall call;
29+
30+
AllocBuffer() {
31+
asExpr() = call and
32+
call.getTarget() instanceof Alloc
33+
}
34+
35+
override Expr getSizeExpr() { result = call.getArgument(0) }
36+
}
37+
38+
/**
39+
* Find accesses of buffers for which we have a size expression.
40+
*/
41+
private class BufferWithSizeConfig extends TaintTracking::Configuration {
42+
BufferWithSizeConfig() { this = "BufferWithSize" }
43+
44+
override predicate isSource(DataFlow::Node n) { n = any(BufferWithSize b) }
45+
46+
override predicate isSink(DataFlow::Node n) { n.asExpr() = any(BufferAccess ae).getPointer() }
47+
48+
override predicate isSanitizer(DataFlow::Node s) {
49+
s = any(BufferWithSize b) and
50+
s.asExpr().getControlFlowScope() instanceof Alloc
51+
}
52+
}
53+
54+
/**
55+
* An access (read or write) to a buffer, provided as a pair of
56+
* a pointer to the buffer and the length of data to be read or written.
57+
* Extend this class to support different kinds of buffer access.
58+
*/
59+
abstract class BufferAccess extends Locatable {
60+
/** Gets the pointer to the buffer being accessed. */
61+
abstract Expr getPointer();
62+
63+
/** Gets the length of the data being read or written by this buffer access. */
64+
abstract Expr getAccessedLength();
65+
}
66+
67+
/**
68+
* A buffer access through an array expression.
69+
*/
70+
class ArrayBufferAccess extends BufferAccess, ArrayExpr {
71+
override Expr getPointer() { result = this.getArrayBase() }
72+
73+
override Expr getAccessedLength() { result = this.getArrayOffset() }
74+
}
75+
76+
/**
77+
* A buffer access through an overloaded array expression.
78+
*/
79+
class OverloadedArrayBufferAccess extends BufferAccess, OverloadedArrayExpr {
80+
override Expr getPointer() { result = this.getQualifier() }
81+
82+
override Expr getAccessedLength() { result = this.getAnArgument() }
83+
}
84+
85+
/**
86+
* A buffer access through pointer arithmetic.
87+
*/
88+
class PointerArithmeticAccess extends BufferAccess, Expr {
89+
PointerArithmeticOperation p;
90+
91+
PointerArithmeticAccess() {
92+
this = p and
93+
p.getAnOperand().getType().getUnspecifiedType() instanceof IntegralType and
94+
not p.getParent() instanceof ComparisonOperation
95+
}
96+
97+
override Expr getPointer() {
98+
result = p.getAnOperand() and
99+
result.getType().getUnspecifiedType() instanceof PointerType
100+
}
101+
102+
override Expr getAccessedLength() {
103+
result = p.getAnOperand() and
104+
result.getType().getUnspecifiedType() instanceof IntegralType
105+
}
106+
}
107+
108+
/**
109+
* A pair of buffer accesses through a call to memcpy.
110+
*/
111+
class MemCpy extends BufferAccess, FunctionCall {
112+
MemCpy() { getTarget().hasName("memcpy") }
113+
114+
override Expr getPointer() {
115+
result = getArgument(0) or
116+
result = getArgument(1)
117+
}
118+
119+
override Expr getAccessedLength() { result = getArgument(2) }
120+
}
121+
122+
class StrncpySizeExpr extends BufferAccess, FunctionCall {
123+
StrncpySizeExpr() { getTarget().hasName("strncpy") }
124+
125+
override Expr getPointer() {
126+
result = getArgument(0) or
127+
result = getArgument(1)
128+
}
129+
130+
override Expr getAccessedLength() { result = getArgument(2) }
131+
}
132+
133+
class RecvSizeExpr extends BufferAccess, FunctionCall {
134+
RecvSizeExpr() { getTarget().hasName("recv") }
135+
136+
override Expr getPointer() { result = getArgument(1) }
137+
138+
override Expr getAccessedLength() { result = getArgument(2) }
139+
}
140+
141+
class SendSizeExpr extends BufferAccess, FunctionCall {
142+
SendSizeExpr() { getTarget().hasName("send") }
143+
144+
override Expr getPointer() { result = getArgument(1) }
145+
146+
override Expr getAccessedLength() { result = getArgument(2) }
147+
}
148+
149+
class SnprintfSizeExpr extends BufferAccess, FunctionCall {
150+
SnprintfSizeExpr() { getTarget().hasName("snprintf") }
151+
152+
override Expr getPointer() { result = getArgument(0) }
153+
154+
override Expr getAccessedLength() { result = getArgument(1) }
155+
}
156+
157+
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
158+
MemcmpSizeExpr() { getTarget().hasName("Memcmp") }
159+
160+
override Expr getPointer() {
161+
result = getArgument(0) or
162+
result = getArgument(1)
163+
}
164+
165+
override Expr getAccessedLength() { result = getArgument(2) }
166+
}
167+
168+
class MallocSizeExpr extends BufferAccess, FunctionCall {
169+
MallocSizeExpr() { getTarget().hasName("malloc") }
170+
171+
override Expr getPointer() { none() }
172+
173+
override Expr getAccessedLength() { result = getArgument(1) }
174+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
int get_number_from_network();
2+
3+
int process_network(int[] buff, int buffSize) {
4+
int i = ntohl(get_number_from_network());
5+
return buff[i];
6+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
uint32_t get_number_from_network();
2+
3+
int process_network(int[] buff, uint32_t buffSize) {
4+
uint32_t i = ntohl(get_number_from_network());
5+
if (i < buffSize) {
6+
return buff[i];
7+
} else {
8+
return -1;
9+
}
10+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import cpp
2+
import semmle.code.cpp.dataflow.DataFlow
3+
import semmle.code.cpp.controlflow.Guards
4+
import BufferAccess
5+
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
6+
7+
class NetworkFunctionCall extends FunctionCall {
8+
NetworkFunctionCall() {
9+
getTarget().hasName("ntohd") or
10+
getTarget().hasName("ntohf") or
11+
getTarget().hasName("ntohl") or
12+
getTarget().hasName("ntohll") or
13+
getTarget().hasName("ntohs")
14+
}
15+
}
16+
17+
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
18+
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
19+
20+
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
21+
22+
override predicate isSink(DataFlow::Node node) {
23+
node.asExpr() = any(BufferAccess ba).getAccessedLength()
24+
}
25+
26+
override predicate isBarrier(DataFlow::Node node) {
27+
exists(GuardCondition gc, GVN gvn |
28+
gc.getAChild*() = gvn.getAnExpr() and
29+
globalValueNumber(node.asExpr()) = gvn and
30+
gc.controls(node.asExpr().getBasicBlock(), _)
31+
)
32+
}
33+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Data received over a network connection may be received in a different byte order than
9+
the byte order used by the local host, making the data difficult to process. To address this,
10+
data received over the wire is usually converted to host byte order by a call to a network-to-host
11+
byte order function, such as <code>ntohl</code>.
12+
</p>
13+
<p>
14+
The use of a network-to-host byte order function is therefore a good indicator that the returned
15+
value is unvalidated data retrieved from the network, and should not be used without further
16+
validation. In particular, the returned value should not be used as an array index or array length
17+
value without validation, which may result in a buffer overflow vulnerability.
18+
</p>
19+
</overview>
20+
21+
<recommendation>
22+
<p>
23+
Validate data returned by network-to-host byte order functions before use and especially before
24+
using the value as an array index or bound.
25+
</p>
26+
</recommendation>
27+
28+
<example>
29+
<p>In the example below, network data is retrieved and passed to <code>ntohl</code> to convert
30+
it to host byte order. The data is then used as an index in an array access expression. However,
31+
there is no validation that the data returned by <code>ntohl</code> is within the bounds of the array,
32+
which could lead to reading outside the bounds of the buffer.
33+
</p>
34+
<sample src="NtohlArrayBad.cpp" />
35+
<p>In the corrected example, the returned data is validated against the known size of the buffer,
36+
before being used as an array index.</p>
37+
<sample src="NtohlArrayGood.cpp" />
38+
</example>
39+
40+
<references>
41+
<li>
42+
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-ntohl">
43+
ntohl - winsock reference
44+
</a>
45+
</li>
46+
<li>
47+
<a href="https://linux.die.net/man/3/ntohl">
48+
ntohl - Linux man page
49+
</a>
50+
</li>
51+
52+
</references>
53+
54+
</qhelp>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @id cpp/network-to-host-function-as-array-bound
3+
* @name Untrusted network-to-host usage
4+
* @description Using the result of a network-to-host byte order function, such as ntohl, as an
5+
* array bound or length value without checking it may result in buffer overflows or
6+
* other vulnerabilties.
7+
* @kind problem
8+
* @problem.severity error
9+
*/
10+
11+
import cpp
12+
import NtohlArrayNoBound
13+
import semmle.code.cpp.dataflow.DataFlow
14+
15+
from NetworkToBufferSizeConfiguration bufConfig, DataFlow::Node source, DataFlow::Node sink
16+
where bufConfig.hasFlow(source, sink)
17+
select sink, "Unchecked use of data from network function $@", source, source.toString()

0 commit comments

Comments
 (0)