Skip to content

Commit e4d0e74

Browse files
committed
C++: some experimental product flow queries
1 parent d0f4c2f commit e4d0e74

File tree

9 files changed

+225
-1
lines changed

9 files changed

+225
-1
lines changed

cpp/ql/lib/experimental/semmle/code/cpp/dataflow/ProductFlow.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module ProductFlow {
2626
) {
2727
isSourcePair(source1.getNode(), source2.getNode()) and
2828
isSinkPair(sink1.getNode(), sink2.getNode()) and
29-
reachablePair(this, source1, source2, sink1, sink2)
29+
reachablePair2(this, source1, source2, sink1, sink2)
3030
}
3131
}
3232

@@ -52,6 +52,10 @@ module ProductFlow {
5252
override predicate isSink(DataFlow::Node sink) {
5353
exists(Configuration conf | conf.isSinkPair(_, sink))
5454
}
55+
56+
override int explorationLimit() {
57+
result = 10
58+
}
5559
}
5660

5761
predicate reachablePair1(
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import cpp
2+
import experimental.semmle.code.cpp.dataflow.ProductFlow
3+
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysis
4+
import experimental.semmle.code.cpp.rangeanalysis.Bound
5+
import experimental.semmle.code.cpp.semantic.SemanticExprSpecific
6+
import semmle.code.cpp.ir.IR
7+
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
8+
import semmle.code.cpp.models.interfaces.Allocation
9+
10+
predicate bounded(Instruction i, Bound b, int delta, boolean upper) {
11+
// TODO: reason
12+
semBounded(getSemanticExpr(i), b, delta, upper, _)
13+
}
14+
15+
class ArraySizeConfiguration extends ProductFlow::Configuration {
16+
ArraySizeConfiguration() { this = "ArraySizeConfiguration" }
17+
18+
override predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2) {
19+
exists(GVN sizeGVN |
20+
source1.asConvertedExpr().(AllocationExpr).getSizeExpr() = sizeGVN.getAnExpr() and
21+
source2.asConvertedExpr() = sizeGVN.getAnExpr()
22+
)
23+
}
24+
25+
override predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2) {
26+
exists(PointerAddInstruction pai, Instruction index, Bound b, int delta |
27+
pai.getRight() = index and
28+
pai.getLeft() = sink1.asInstruction() and
29+
bounded(index, b, delta, true) and
30+
sink2.asInstruction() = b.getInstruction())
31+
}
32+
}
33+
34+
predicate hasFlow1(DataFlow::PathNode source, DataFlow::PathNode sink) {
35+
any(ProductFlow::Conf1 conf).hasFlowPath(source, sink)
36+
}
37+
38+
predicate hasFlow2(DataFlow2::PathNode source, DataFlow2::PathNode sink) {
39+
any(ProductFlow::Conf2 conf).hasFlowPath(source, sink)
40+
}
41+
42+
43+
predicate hasPartialFlow2(DataFlow2::PartialPathNode source, DataFlow2::PartialPathNode sink) {
44+
any(ProductFlow::Conf2 conf).hasPartialFlow(source, sink, _)
45+
}
46+
47+
from ArraySizeConfiguration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1, DataFlow2::PathNode sink2
48+
where conf.hasFlowPath(source1, source2, sink1, sink2)
49+
select source1, source2, sink1, sink2
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import cpp
2+
import experimental.semmle.code.cpp.dataflow.ProductFlow
3+
import semmle.code.cpp.ir.IR
4+
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
5+
import semmle.code.cpp.models.interfaces.Allocation
6+
import semmle.code.cpp.models.interfaces.ArrayFunction
7+
8+
class StringSizeConfiguration extends ProductFlow::Configuration {
9+
StringSizeConfiguration() { this = "StringSizeConfiguration" }
10+
11+
override predicate isSourcePair(DataFlow::Node bufSource, DataFlow::Node sizeSource) {
12+
exists(
13+
GVN sizeGVN // TODO: use-use flow instead of GVN
14+
|
15+
bufSource.asConvertedExpr().(AllocationExpr).getSizeExpr() = sizeGVN.getAnExpr() and
16+
sizeSource.asConvertedExpr() = sizeGVN.getAnExpr()
17+
)
18+
}
19+
20+
override predicate isSinkPair(DataFlow::Node bufSink, DataFlow::Node sizeSink) {
21+
exists(CallInstruction c, int bufIndex, int sizeIndex |
22+
c.getStaticCallTarget().(ArrayFunction).hasArrayWithVariableSize(bufIndex, sizeIndex) and
23+
c.getArgument(bufIndex) = bufSink.asInstruction() and
24+
c.getArgument(sizeIndex) = sizeSink.asInstruction()
25+
)
26+
}
27+
}
28+
29+
from StringSizeConfiguration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1, DataFlow2::PathNode sink2
30+
where conf.hasFlowPath(source1, source2, sink1, sink2)
31+
select source1, source2, sink1, sink2
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.cpp:19:19:19:24 | call to malloc | test.cpp:18:17:18:20 | size | test.cpp:26:18:26:23 | string | test.cpp:26:31:26:39 | (size_t)... |
2+
| test.cpp:19:19:19:24 | call to malloc | test.cpp:18:17:18:20 | size | test.cpp:30:18:30:23 | string | test.cpp:30:31:30:39 | (size_t)... |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Likely Bugs/OverrunWriteProductFlow.ql
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
typedef unsigned long long size_t;
3+
int sprintf(char *s, const char *format, ...);
4+
int snprintf(char *s, size_t n, const char *format, ...);
5+
int scanf(const char *format, ...);
6+
int sscanf(const char *s, const char *format, ...);
7+
char *malloc(size_t size);
8+
char *strncpy(char *dst, const char *src, size_t n);
9+
10+
typedef struct
11+
{
12+
char *string;
13+
int size;
14+
} string_t;
15+
16+
string_t *mk_string_t(int size) {
17+
string_t *str = (string_t *) malloc(sizeof(string_t));
18+
str->size = size;
19+
str->string = malloc(size);
20+
return str;
21+
}
22+
23+
void test1(int size, char *buf) {
24+
string_t *str = mk_string_t(size);
25+
26+
strncpy(str->string, buf, str->size);
27+
}
28+
29+
void strncpy_wrapper(string_t *str, char *buf) {
30+
strncpy(str->string, buf, str->size);
31+
}
32+
33+
void test2(int size, char *buf) {
34+
string_t *str = mk_string_t(size);
35+
strncpy_wrapper(str, buf);
36+
}
37+

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Likely Bugs/ArrayAccessProductFlow.ql
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
char *malloc(int size);
2+
3+
void test1(int size) {
4+
char *arr = malloc(size);
5+
for (int i = 0; i < size; i++) {
6+
arr[i] = 0;
7+
}
8+
9+
for (int i = 0; i <= size; i++) {
10+
arr[i] = i;
11+
}
12+
}
13+
14+
typedef struct {
15+
int size;
16+
char *p;
17+
} array_t;
18+
19+
array_t mk_array(int size) {
20+
array_t arr;
21+
arr.size = size;
22+
arr.p = malloc(size);
23+
24+
return arr;
25+
}
26+
27+
void test2(int size) {
28+
array_t arr = mk_array(size);
29+
30+
for (int i = 0; i < arr.size; i++) {
31+
arr.p[i] = 0;
32+
}
33+
34+
for (int i = 0; i <= arr.size; i++) {
35+
arr.p[i] = i;
36+
}
37+
}
38+
39+
void test3_callee(array_t arr) {
40+
for (int i = 0; i < arr.size; i++) {
41+
arr.p[i] = 0;
42+
}
43+
44+
for (int i = 0; i <= arr.size; i++) {
45+
arr.p[i] = i;
46+
}
47+
}
48+
49+
void test3(int size) {
50+
test3_callee(mk_array(size));
51+
}
52+
53+
void test4(int size) {
54+
array_t arr;
55+
arr.size = size;
56+
arr.p = malloc(size);
57+
58+
for (int i = 0; i < arr.size; i++) {
59+
arr.p[i] = 0;
60+
}
61+
62+
for (int i = 0; i <= arr.size; i++) {
63+
arr.p[i] = i;
64+
}
65+
}
66+
67+
array_t *mk_array_p(int size) {
68+
array_t *arr = (array_t*) malloc(sizeof(array_t));
69+
arr->size = size;
70+
arr->p = malloc(size);
71+
72+
return arr;
73+
}
74+
75+
void test5(int size) {
76+
array_t *arr = mk_array_p(size);
77+
78+
for (int i = 0; i < arr->size; i++) {
79+
arr->p[i] = 0;
80+
}
81+
82+
for (int i = 0; i <= arr->size; i++) {
83+
arr->p[i] = i;
84+
}
85+
}
86+
87+
void test6_callee(array_t *arr) {
88+
for (int i = 0; i < arr->size; i++) {
89+
arr->p[i] = 0;
90+
}
91+
92+
for (int i = 0; i <= arr->size; i++) {
93+
arr->p[i] = i;
94+
}
95+
}
96+
97+
void test6(int size) {
98+
test6_callee(mk_array_p(size));
99+
}

0 commit comments

Comments
 (0)