C++: some experimental product flow queries

This commit is contained in:
Robert Marsh
2022-08-09 10:19:45 -04:00
parent d0f4c2f823
commit e4d0e7431c
9 changed files with 225 additions and 1 deletions

View File

@@ -26,7 +26,7 @@ module ProductFlow {
) {
isSourcePair(source1.getNode(), source2.getNode()) and
isSinkPair(sink1.getNode(), sink2.getNode()) and
reachablePair(this, source1, source2, sink1, sink2)
reachablePair2(this, source1, source2, sink1, sink2)
}
}
@@ -52,6 +52,10 @@ module ProductFlow {
override predicate isSink(DataFlow::Node sink) {
exists(Configuration conf | conf.isSinkPair(_, sink))
}
override int explorationLimit() {
result = 10
}
}
predicate reachablePair1(

View File

@@ -0,0 +1,49 @@
import cpp
import experimental.semmle.code.cpp.dataflow.ProductFlow
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysis
import experimental.semmle.code.cpp.rangeanalysis.Bound
import experimental.semmle.code.cpp.semantic.SemanticExprSpecific
import semmle.code.cpp.ir.IR
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.models.interfaces.Allocation
predicate bounded(Instruction i, Bound b, int delta, boolean upper) {
// TODO: reason
semBounded(getSemanticExpr(i), b, delta, upper, _)
}
class ArraySizeConfiguration extends ProductFlow::Configuration {
ArraySizeConfiguration() { this = "ArraySizeConfiguration" }
override predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2) {
exists(GVN sizeGVN |
source1.asConvertedExpr().(AllocationExpr).getSizeExpr() = sizeGVN.getAnExpr() and
source2.asConvertedExpr() = sizeGVN.getAnExpr()
)
}
override predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2) {
exists(PointerAddInstruction pai, Instruction index, Bound b, int delta |
pai.getRight() = index and
pai.getLeft() = sink1.asInstruction() and
bounded(index, b, delta, true) and
sink2.asInstruction() = b.getInstruction())
}
}
predicate hasFlow1(DataFlow::PathNode source, DataFlow::PathNode sink) {
any(ProductFlow::Conf1 conf).hasFlowPath(source, sink)
}
predicate hasFlow2(DataFlow2::PathNode source, DataFlow2::PathNode sink) {
any(ProductFlow::Conf2 conf).hasFlowPath(source, sink)
}
predicate hasPartialFlow2(DataFlow2::PartialPathNode source, DataFlow2::PartialPathNode sink) {
any(ProductFlow::Conf2 conf).hasPartialFlow(source, sink, _)
}
from ArraySizeConfiguration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1, DataFlow2::PathNode sink2
where conf.hasFlowPath(source1, source2, sink1, sink2)
select source1, source2, sink1, sink2

View File

@@ -0,0 +1,31 @@
import cpp
import experimental.semmle.code.cpp.dataflow.ProductFlow
import semmle.code.cpp.ir.IR
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.ArrayFunction
class StringSizeConfiguration extends ProductFlow::Configuration {
StringSizeConfiguration() { this = "StringSizeConfiguration" }
override predicate isSourcePair(DataFlow::Node bufSource, DataFlow::Node sizeSource) {
exists(
GVN sizeGVN // TODO: use-use flow instead of GVN
|
bufSource.asConvertedExpr().(AllocationExpr).getSizeExpr() = sizeGVN.getAnExpr() and
sizeSource.asConvertedExpr() = sizeGVN.getAnExpr()
)
}
override predicate isSinkPair(DataFlow::Node bufSink, DataFlow::Node sizeSink) {
exists(CallInstruction c, int bufIndex, int sizeIndex |
c.getStaticCallTarget().(ArrayFunction).hasArrayWithVariableSize(bufIndex, sizeIndex) and
c.getArgument(bufIndex) = bufSink.asInstruction() and
c.getArgument(sizeIndex) = sizeSink.asInstruction()
)
}
}
from StringSizeConfiguration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1, DataFlow2::PathNode sink2
where conf.hasFlowPath(source1, source2, sink1, sink2)
select source1, source2, sink1, sink2

View File

@@ -0,0 +1,2 @@
| 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)... |
| 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)... |

View File

@@ -0,0 +1 @@
experimental/Likely Bugs/OverrunWriteProductFlow.ql

View File

@@ -0,0 +1,37 @@
typedef unsigned long long size_t;
int sprintf(char *s, const char *format, ...);
int snprintf(char *s, size_t n, const char *format, ...);
int scanf(const char *format, ...);
int sscanf(const char *s, const char *format, ...);
char *malloc(size_t size);
char *strncpy(char *dst, const char *src, size_t n);
typedef struct
{
char *string;
int size;
} string_t;
string_t *mk_string_t(int size) {
string_t *str = (string_t *) malloc(sizeof(string_t));
str->size = size;
str->string = malloc(size);
return str;
}
void test1(int size, char *buf) {
string_t *str = mk_string_t(size);
strncpy(str->string, buf, str->size);
}
void strncpy_wrapper(string_t *str, char *buf) {
strncpy(str->string, buf, str->size);
}
void test2(int size, char *buf) {
string_t *str = mk_string_t(size);
strncpy_wrapper(str, buf);
}

View File

@@ -0,0 +1 @@
experimental/Likely Bugs/ArrayAccessProductFlow.ql

View File

@@ -0,0 +1,99 @@
char *malloc(int size);
void test1(int size) {
char *arr = malloc(size);
for (int i = 0; i < size; i++) {
arr[i] = 0;
}
for (int i = 0; i <= size; i++) {
arr[i] = i;
}
}
typedef struct {
int size;
char *p;
} array_t;
array_t mk_array(int size) {
array_t arr;
arr.size = size;
arr.p = malloc(size);
return arr;
}
void test2(int size) {
array_t arr = mk_array(size);
for (int i = 0; i < arr.size; i++) {
arr.p[i] = 0;
}
for (int i = 0; i <= arr.size; i++) {
arr.p[i] = i;
}
}
void test3_callee(array_t arr) {
for (int i = 0; i < arr.size; i++) {
arr.p[i] = 0;
}
for (int i = 0; i <= arr.size; i++) {
arr.p[i] = i;
}
}
void test3(int size) {
test3_callee(mk_array(size));
}
void test4(int size) {
array_t arr;
arr.size = size;
arr.p = malloc(size);
for (int i = 0; i < arr.size; i++) {
arr.p[i] = 0;
}
for (int i = 0; i <= arr.size; i++) {
arr.p[i] = i;
}
}
array_t *mk_array_p(int size) {
array_t *arr = (array_t*) malloc(sizeof(array_t));
arr->size = size;
arr->p = malloc(size);
return arr;
}
void test5(int size) {
array_t *arr = mk_array_p(size);
for (int i = 0; i < arr->size; i++) {
arr->p[i] = 0;
}
for (int i = 0; i <= arr->size; i++) {
arr->p[i] = i;
}
}
void test6_callee(array_t *arr) {
for (int i = 0; i < arr->size; i++) {
arr->p[i] = 0;
}
for (int i = 0; i <= arr->size; i++) {
arr->p[i] = i;
}
}
void test6(int size) {
test6_callee(mk_array_p(size));
}