Skip to content

Commit 63a2657

Browse files
committed
Merge branch 'main' into inline-taint-tests
2 parents d607c13 + 1ed11b2 commit 63a2657

File tree

136 files changed

+3828
-1640
lines changed

Some content is hidden

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

136 files changed

+3828
-1640
lines changed

.github/workflows/close-stale.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ jobs:
1515
- uses: actions/stale@v3
1616
with:
1717
repo-token: ${{ secrets.GITHUB_TOKEN }}
18-
stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `stale` label in order to avoid having this issue closed in 7 days.'
18+
stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.'
1919
close-issue-message: 'This issue was closed because it has been inactive for 7 days.'
2020
days-before-stale: 14
2121
days-before-close: 7
22-
only-labels: question
23-
22+
only-labels: awaiting-response
23+
2424
# do not mark PRs as stale
2525
days-before-pr-stale: -1
2626
days-before-pr-close: -1
27-
27+
2828
# Uncomment for dry-run
2929
# debug-only: true
3030
# operations-per-run: 1000
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm
2+
* The queries cpp/tainted-arithmetic, cpp/uncontrolled-arithmetic, and cpp/arithmetic-with-extreme-values have been improved to produce fewer false positives.

cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import cpp
1515
import semmle.code.cpp.dataflow.EscapesTree
16+
import semmle.code.cpp.models.interfaces.PointerWrapper
1617
import semmle.code.cpp.dataflow.DataFlow
1718

1819
/**
@@ -39,6 +40,10 @@ predicate hasNontrivialConversion(Expr e) {
3940
e instanceof ParenthesisExpr
4041
)
4142
or
43+
// A smart pointer can be stack-allocated while the data it points to is heap-allocated.
44+
// So we exclude such "conversions" from this predicate.
45+
e = any(PointerWrapper wrapper).getAnUnwrapperFunction().getACallToThisFunction()
46+
or
4247
hasNontrivialConversion(e.getConversion())
4348
}
4449

cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql

Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,32 @@
1111
*/
1212

1313
import cpp
14+
import semmle.code.cpp.models.implementations.Strcat
1415
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
1516

1617
/**
17-
* A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`.
18+
* Holds if `call` is a call to `strncat` such that `sizeArg` and `destArg` are the size and
19+
* destination arguments, respectively.
1820
*/
19-
class WrongCallStrncat extends FunctionCall {
20-
Expr leftsomeExpr;
21-
22-
WrongCallStrncat() {
23-
this.getTarget().hasGlobalOrStdName("strncat") and
24-
// the expression of the first argument in `strncat` and `strnlen` is identical
25-
globalValueNumber(this.getArgument(0)) =
26-
globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and
27-
// using a string constant often speaks of manually calculating the length of the required buffer.
28-
(
29-
not this.getArgument(1) instanceof StringLiteral and
30-
not this.getArgument(1) instanceof CharLiteral
31-
) and
32-
// for use in predicates
33-
leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand()
34-
}
35-
36-
/**
37-
* Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`.
38-
*/
39-
predicate isExpressionEqualSizeof() {
40-
// the left side of the expression `someExpr` is `sizeof(buf)`.
41-
globalValueNumber(this.getArgument(0)) =
42-
globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand())
43-
or
44-
// value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array.
45-
leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize()
46-
}
47-
48-
/**
49-
* Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer.
50-
*/
51-
predicate isVariableEqualValueSizegBuffer() {
52-
// the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`.
53-
exists(AllocationExpr alc |
54-
leftsomeExpr.(VariableAccess).getTarget() =
55-
alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget()
56-
)
57-
}
21+
predicate interestringCallWithArgs(Call call, Expr sizeArg, Expr destArg) {
22+
exists(StrcatFunction strcat |
23+
strcat = call.getTarget() and
24+
sizeArg = call.getArgument(strcat.getParamSize()) and
25+
destArg = call.getArgument(strcat.getParamDest())
26+
)
5827
}
5928

60-
from WrongCallStrncat sc
29+
from FunctionCall call, Expr sizeArg, Expr destArg, SubExpr sub, int n
6130
where
62-
sc.isExpressionEqualSizeof() or
63-
sc.isVariableEqualValueSizegBuffer()
64-
select sc, "if the used buffer is full, writing out of the buffer is possible"
31+
interestringCallWithArgs(call, sizeArg, destArg) and
32+
// The destination buffer is an array of size n
33+
destArg.getUnspecifiedType().(ArrayType).getSize() = n and
34+
// The size argument is equivalent to a subtraction
35+
globalValueNumber(sizeArg).getAnExpr() = sub and
36+
// ... where the left side of the subtraction is the constant n
37+
globalValueNumber(sub.getLeftOperand()).getAnExpr().getValue().toInt() = n and
38+
// ... and the right side of the subtraction is a call to `strlen` where the argument is the
39+
// destination buffer.
40+
globalValueNumber(sub.getRightOperand()).getAnExpr().(StrlenCall).getStringExpr() =
41+
globalValueNumber(destArg).getAnExpr()
42+
select call, "Possible out-of-bounds write due to incorrect size argument."

cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,20 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper {
1818
}
1919

2020
/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */
21-
private class PointerWrapperDataFlow extends DataFlowFunction {
22-
PointerWrapperDataFlow() {
21+
private class PointerWrapperFlow extends TaintFunction, DataFlowFunction {
22+
PointerWrapperFlow() {
2323
this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and
2424
not this.getUnspecifiedType() instanceof ReferenceType
2525
}
2626

27-
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
28-
input.isQualifierAddress() and output.isReturnValue()
29-
or
30-
input.isQualifierObject() and output.isReturnValueDeref()
31-
or
27+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
3228
input.isReturnValueDeref() and
3329
output.isQualifierObject()
3430
}
31+
32+
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
33+
input.isQualifierObject() and output.isReturnValue()
34+
}
3535
}
3636

3737
/**

cpp/ql/src/semmle/code/cpp/security/Overflow.qll

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import cpp
77
import semmle.code.cpp.controlflow.Dominance
8+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
9+
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
810

911
/**
1012
* Holds if the value of `use` is guarded using `abs`.
@@ -94,9 +96,15 @@ predicate guardedGreater(Operation e, Expr use) {
9496
VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() }
9597

9698
/**
97-
* Holds if `e` is not guarded against overflow by `use`.
99+
* Holds if `e` potentially overflows and `use` is an operand of `e` that is not guarded.
98100
*/
99101
predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
102+
(
103+
convertedExprMightOverflowPositively(e)
104+
or
105+
// Ensure that the predicate holds when range analysis cannot determine an upper bound
106+
upperBound(e.getFullyConverted()) = exprMaxVal(e.getFullyConverted())
107+
) and
100108
use = e.getAnOperand() and
101109
exists(LocalScopeVariable v | use.getTarget() = v |
102110
// overflow possible if large
@@ -115,9 +123,15 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
115123
}
116124

117125
/**
118-
* Holds if `e` is not guarded against underflow by `use`.
126+
* Holds if `e` potentially underflows and `use` is an operand of `e` that is not guarded.
119127
*/
120128
predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) {
129+
(
130+
convertedExprMightOverflowNegatively(e)
131+
or
132+
// Ensure that the predicate holds when range analysis cannot determine a lower bound
133+
lowerBound(e.getFullyConverted()) = exprMinVal(e.getFullyConverted())
134+
) and
121135
use = e.getAnOperand() and
122136
exists(LocalScopeVariable v | use.getTarget() = v |
123137
// underflow possible if use is left operand and small
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
| test.c:42:3:42:24 | ... = ... | potential unsafe or redundant assignment. |
2-
| test.c:43:3:43:40 | ... = ... | potential unsafe or redundant assignment. |
3-
| test.c:44:3:44:40 | ... = ... | potential unsafe or redundant assignment. |
4-
| test.c:45:3:45:44 | ... = ... | potential unsafe or redundant assignment. |
5-
| test.c:46:3:46:44 | ... = ... | potential unsafe or redundant assignment. |
6-
| test.c:47:3:47:48 | ... = ... | potential unsafe or redundant assignment. |
7-
| test.c:48:3:48:48 | ... = ... | potential unsafe or redundant assignment. |
8-
| test.c:49:3:49:50 | ... = ... | potential unsafe or redundant assignment. |
9-
| test.c:50:3:50:50 | ... = ... | potential unsafe or redundant assignment. |
1+
| test.c:54:3:54:24 | ... = ... | potential unsafe or redundant assignment. |
2+
| test.c:55:3:55:40 | ... = ... | potential unsafe or redundant assignment. |
3+
| test.c:56:3:56:44 | ... = ... | potential unsafe or redundant assignment. |
4+
| test.c:57:3:57:44 | ... = ... | potential unsafe or redundant assignment. |
5+
| test.c:58:3:58:48 | ... = ... | potential unsafe or redundant assignment. |
6+
| test.c:59:3:59:48 | ... = ... | potential unsafe or redundant assignment. |
7+
| test.c:60:3:60:52 | ... = ... | potential unsafe or redundant assignment. |
8+
| test.c:61:3:61:50 | ... = ... | potential unsafe or redundant assignment. |
9+
| test.c:62:3:62:54 | ... = ... | potential unsafe or redundant assignment. |
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
| test.c:4:3:4:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
2-
| test.c:11:3:11:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
3-
| test.c:19:3:19:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
1+
| test.c:8:3:8:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
2+
| test.c:9:3:9:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
3+
| test.c:17:3:17:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
4+
| test.c:18:3:18:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
5+
| test.c:46:3:46:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,84 @@
1-
void workFunction_0(char *s) {
1+
char * strncat(char*, const char*, unsigned);
2+
unsigned strlen(const char*);
3+
void* malloc(unsigned);
4+
5+
void strncat_test1(char *s) {
26
char buf[80];
3-
strncat(buf, s, sizeof(buf)-strlen(buf)-1); // GOOD
4-
strncat(buf, s, sizeof(buf)-strlen(buf)); // BAD
5-
strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD [NOT DETECTED]
7+
strncat(buf, s, sizeof(buf) - strlen(buf) - 1); // GOOD
8+
strncat(buf, s, sizeof(buf) - strlen(buf)); // BAD
9+
strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD
610
}
7-
void workFunction_1(char *s) {
11+
812
#define MAX_SIZE 80
13+
14+
void strncat_test2(char *s) {
915
char buf[MAX_SIZE];
10-
strncat(buf, s, MAX_SIZE-strlen(buf)-1); // GOOD
11-
strncat(buf, s, MAX_SIZE-strlen(buf)); // BAD
12-
strncat(buf, "fix", MAX_SIZE-strlen(buf)); // BAD [NOT DETECTED]
16+
strncat(buf, s, MAX_SIZE - strlen(buf) - 1); // GOOD
17+
strncat(buf, s, MAX_SIZE - strlen(buf)); // BAD
18+
strncat(buf, "fix", MAX_SIZE - strlen(buf)); // BAD
1319
}
14-
void workFunction_2_0(char *s) {
15-
char * buf;
16-
int len=80;
17-
buf = (char *) malloc(len);
18-
strncat(buf, s, len-strlen(buf)-1); // GOOD
19-
strncat(buf, s, len-strlen(buf)); // BAD
20-
strncat(buf, "fix", len-strlen(buf)); // BAD [NOT DETECTED]
20+
21+
void strncat_test3(char *s) {
22+
int len = 80;
23+
char* buf = (char *) malloc(len);
24+
strncat(buf, s, len - strlen(buf) - 1); // GOOD
25+
strncat(buf, s, len - strlen(buf)); // BAD [NOT DETECTED]
26+
strncat(buf, "fix", len - strlen(buf)); // BAD [NOT DETECTED]
2127
}
22-
void workFunction_2_1(char *s) {
23-
char * buf;
24-
int len=80;
25-
buf = (char *) malloc(len+1);
26-
strncat(buf, s, len-strlen(buf)-1); // GOOD
27-
strncat(buf, s, len-strlen(buf)); // GOOD
28+
29+
void strncat_test4(char *s) {
30+
int len = 80;
31+
char* buf = (char *) malloc(len + 1);
32+
strncat(buf, s, len - strlen(buf) - 1); // GOOD
33+
strncat(buf, s, len - strlen(buf)); // GOOD
2834
}
2935

3036
struct buffers
3137
{
32-
unsigned char buff1[50];
33-
unsigned char *buff2;
38+
unsigned char array[50];
39+
unsigned char *pointer;
3440
} globalBuff1,*globalBuff2,globalBuff1_c,*globalBuff2_c;
3541

42+
void strncat_test5(char* s, struct buffers* buffers) {
43+
unsigned len_array = strlen(buffers->array);
44+
unsigned max_size = sizeof(buffers->array);
45+
unsigned free_size = max_size - len_array;
46+
strncat(buffers->array, s, free_size); // BAD
47+
}
3648

37-
void badFunc0(){
49+
void strlen_test1(){
3850
unsigned char buff1[12];
3951
struct buffers buffAll;
4052
struct buffers * buffAll1;
4153

4254
buff1[strlen(buff1)]=0; // BAD
43-
buffAll.buff1[strlen(buffAll.buff1)]=0; // BAD
44-
buffAll.buff2[strlen(buffAll.buff2)]=0; // BAD
45-
buffAll1->buff1[strlen(buffAll1->buff1)]=0; // BAD
46-
buffAll1->buff2[strlen(buffAll1->buff2)]=0; // BAD
47-
globalBuff1.buff1[strlen(globalBuff1.buff1)]=0; // BAD
48-
globalBuff1.buff2[strlen(globalBuff1.buff2)]=0; // BAD
49-
globalBuff2->buff1[strlen(globalBuff2->buff1)]=0; // BAD
50-
globalBuff2->buff2[strlen(globalBuff2->buff2)]=0; // BAD
55+
buffAll.array[strlen(buffAll.array)]=0; // BAD
56+
buffAll.pointer[strlen(buffAll.pointer)]=0; // BAD
57+
buffAll1->array[strlen(buffAll1->array)]=0; // BAD
58+
buffAll1->pointer[strlen(buffAll1->pointer)]=0; // BAD
59+
globalBuff1.array[strlen(globalBuff1.array)]=0; // BAD
60+
globalBuff1.pointer[strlen(globalBuff1.pointer)]=0; // BAD
61+
globalBuff2->array[strlen(globalBuff2->array)]=0; // BAD
62+
globalBuff2->pointer[strlen(globalBuff2->pointer)]=0; // BAD
5163
}
52-
void noBadFunc0(){
64+
65+
void strlen_test2(){
5366
unsigned char buff1[12],buff1_c[12];
5467
struct buffers buffAll,buffAll_c;
5568
struct buffers * buffAll1,*buffAll1_c;
5669

5770
buff1[strlen(buff1_c)]=0; // GOOD
58-
buffAll.buff1[strlen(buffAll_c.buff1)]=0; // GOOD
59-
buffAll.buff2[strlen(buffAll.buff1)]=0; // GOOD
60-
buffAll1->buff1[strlen(buffAll1_c->buff1)]=0; // GOOD
61-
buffAll1->buff2[strlen(buffAll1->buff1)]=0; // GOOD
62-
globalBuff1.buff1[strlen(globalBuff1_c.buff1)]=0; // GOOD
63-
globalBuff1.buff2[strlen(globalBuff1.buff1)]=0; // GOOD
64-
globalBuff2->buff1[strlen(globalBuff2_c->buff1)]=0; // GOOD
65-
globalBuff2->buff2[strlen(globalBuff2->buff1)]=0; // GOOD
71+
buffAll.array[strlen(buffAll_c.array)]=0; // GOOD
72+
buffAll.pointer[strlen(buffAll.array)]=0; // GOOD
73+
buffAll1->array[strlen(buffAll1_c->array)]=0; // GOOD
74+
buffAll1->pointer[strlen(buffAll1->array)]=0; // GOOD
75+
globalBuff1.array[strlen(globalBuff1_c.array)]=0; // GOOD
76+
globalBuff1.pointer[strlen(globalBuff1.array)]=0; // GOOD
77+
globalBuff2->array[strlen(globalBuff2_c->array)]=0; // GOOD
78+
globalBuff2->pointer[strlen(globalBuff2->array)]=0; // GOOD
6679
}
67-
void goodFunc0(){
80+
81+
void strlen_test3(){
6882
unsigned char buffer[12];
6983
int i;
7084
for(i = 0; i < 6; i++)

0 commit comments

Comments
 (0)