mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
C++: some experimental product flow queries
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)... |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Likely Bugs/OverrunWriteProductFlow.ql
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Likely Bugs/ArrayAccessProductFlow.ql
|
||||
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user