Skip to content

Commit 68d9c84

Browse files
authored
Merge branch 'main' into add-cwe-208
2 parents 01577da + 36bdadf commit 68d9c84

File tree

44 files changed

+2052
-1208
lines changed

Some content is hidden

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

44 files changed

+2052
-1208
lines changed

cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import semmle.code.cpp.security.BufferWrite
2424
from BufferWrite bw, int destSize
2525
where
2626
bw.hasExplicitLimit() and // has an explicit size limit
27-
destSize = getBufferSize(bw.getDest(), _) and
27+
destSize = max(getBufferSize(bw.getDest(), _)) and
2828
bw.getExplicitLimit() > destSize // but it's larger than the destination
2929
select bw,
3030
"This '" + bw.getBWDesc() + "' operation is limited to " + bw.getExplicitLimit() +
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `cpp/badly-bounded-write` query could report false positives when a pointer was first initialized with a literal and later assigned a dynamically allocated array. These false positives now no longer occur.

cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/VeryLikelyOverrunWrite.expected

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
| tests2.cpp:17:3:17:8 | call to wcscpy | This 'call to wcscpy' operation requires 12 bytes but the destination is only 8 bytes. |
2-
| tests2.cpp:22:3:22:8 | call to wcscpy | This 'call to wcscpy' operation requires 16 bytes but the destination is only 12 bytes. |
3-
| tests2.cpp:27:3:27:8 | call to wcscpy | This 'call to wcscpy' operation requires 20 bytes but the destination is only 16 bytes. |
4-
| tests2.cpp:31:3:31:8 | call to wcscpy | This 'call to wcscpy' operation requires 24 bytes but the destination is only 20 bytes. |
5-
| tests2.cpp:36:3:36:8 | call to wcscpy | This 'call to wcscpy' operation requires 28 bytes but the destination is only 24 bytes. |
6-
| tests2.cpp:41:3:41:8 | call to wcscpy | This 'call to wcscpy' operation requires 32 bytes but the destination is only 28 bytes. |
7-
| tests2.cpp:46:3:46:8 | call to wcscpy | This 'call to wcscpy' operation requires 36 bytes but the destination is only 32 bytes. |
1+
| tests2.cpp:18:3:18:8 | call to wcscpy | This 'call to wcscpy' operation requires 12 bytes but the destination is only 8 bytes. |
2+
| tests2.cpp:23:3:23:8 | call to wcscpy | This 'call to wcscpy' operation requires 16 bytes but the destination is only 12 bytes. |
3+
| tests2.cpp:28:3:28:8 | call to wcscpy | This 'call to wcscpy' operation requires 20 bytes but the destination is only 16 bytes. |
4+
| tests2.cpp:32:3:32:8 | call to wcscpy | This 'call to wcscpy' operation requires 24 bytes but the destination is only 20 bytes. |
5+
| tests2.cpp:37:3:37:8 | call to wcscpy | This 'call to wcscpy' operation requires 28 bytes but the destination is only 24 bytes. |
6+
| tests2.cpp:42:3:42:8 | call to wcscpy | This 'call to wcscpy' operation requires 32 bytes but the destination is only 28 bytes. |
7+
| tests2.cpp:47:3:47:8 | call to wcscpy | This 'call to wcscpy' operation requires 36 bytes but the destination is only 32 bytes. |
88
| tests.c:54:3:54:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
99
| tests.c:58:3:58:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
1010
| tests.c:62:17:62:24 | buffer10 | This 'scanf string argument' operation requires 11 bytes but the destination is only 10 bytes. |

cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/tests2.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ void *realloc(void *ptr, size_t size);
66
void *calloc(size_t nmemb, size_t size);
77
void free(void *ptr);
88
wchar_t *wcscpy(wchar_t *s1, const wchar_t *s2);
9+
int snprintf(char *s, size_t n, const char *format, ...);
910

1011
// --- Semmle tests ---
1112

@@ -46,3 +47,18 @@ void tests2() {
4647
wcscpy(buffer, L"12345678"); // BAD: buffer overflow
4748
delete [] buffer;
4849
}
50+
51+
char* dest1 = "a";
52+
char* dest2 = "abcdefghijklmnopqrstuvwxyz";
53+
54+
void test3() {
55+
const char src[] = "abcdefghijkl";
56+
dest1 = (char*)malloc(sizeof(src));
57+
if (!dest1)
58+
return;
59+
snprintf(dest1, sizeof(src), "%s", src); // GOOD
60+
dest2 = (char*)malloc(3);
61+
if (!dest2)
62+
return;
63+
snprintf(dest2, sizeof(src), "%s", src); // BAD [NOT DETECTED]: buffer overflow
64+
}

csharp/ql/lib/Linq/Helpers.qll

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
* Provides helper classes and methods related to LINQ.
44
*/
55

6-
import csharp
6+
private import csharp
7+
private import semmle.code.csharp.frameworks.system.collections.Generic as GenericCollections
8+
private import semmle.code.csharp.frameworks.system.Collections as Collections
79

810
//#################### PREDICATES ####################
911
private Stmt firstStmt(ForeachStmt fes) {
@@ -29,13 +31,40 @@ predicate isIEnumerableType(ValueOrRefType t) {
2931
)
3032
}
3133

34+
/**
35+
* A class of foreach statements where the iterable expression
36+
* supports the use of the LINQ extension methods on `IEnumerable<T>`.
37+
*/
38+
class ForeachStmtGenericEnumerable extends ForeachStmt {
39+
ForeachStmtGenericEnumerable() {
40+
exists(ValueOrRefType t | t = this.getIterableExpr().getType() |
41+
t.getABaseType*().getUnboundDeclaration() instanceof
42+
GenericCollections::SystemCollectionsGenericIEnumerableTInterface or
43+
t.(ArrayType).getRank() = 1
44+
)
45+
}
46+
}
47+
48+
/**
49+
* A class of foreach statements where the iterable expression
50+
* supports the use of the LINQ extension methods on `IEnumerable`.
51+
*/
52+
class ForeachStmtEnumerable extends ForeachStmt {
53+
ForeachStmtEnumerable() {
54+
exists(ValueOrRefType t | t = this.getIterableExpr().getType() |
55+
t.getABaseType*() instanceof Collections::SystemCollectionsIEnumerableInterface or
56+
t.(ArrayType).getRank() = 1
57+
)
58+
}
59+
}
60+
3261
/**
3362
* Holds if `foreach` statement `fes` could be converted to a `.All()` call.
3463
* That is, the `ForeachStmt` contains a single `if` with a condition that
3564
* accesses the loop variable and with a body that assigns `false` to a variable
3665
* and `break`s out of the `foreach`.
3766
*/
38-
predicate missedAllOpportunity(ForeachStmt fes) {
67+
predicate missedAllOpportunity(ForeachStmtGenericEnumerable fes) {
3968
exists(IfStmt is |
4069
// The loop contains an if statement with no else case, and nothing else.
4170
is = firstStmt(fes) and
@@ -54,12 +83,12 @@ predicate missedAllOpportunity(ForeachStmt fes) {
5483
}
5584

5685
/**
57-
* Holds if `foreach` statement `fes` could be converted to a `.Cast()` call.
86+
* Holds if the `foreach` statement `fes` can be converted to a `.Cast()` call.
5887
* That is, the loop variable is accessed only in the first statement of the
59-
* block, and the access is a cast. The first statement needs to be a
60-
* `LocalVariableDeclStmt`.
88+
* block, the access is a cast, and the first statement is a
89+
* local variable declaration statement `s`.
6190
*/
62-
predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
91+
predicate missedCastOpportunity(ForeachStmtEnumerable fes, LocalVariableDeclStmt s) {
6392
s = firstStmt(fes) and
6493
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
6594
va = s.getAVariableDeclExpr().getAChildExpr*()
@@ -71,12 +100,12 @@ predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
71100
}
72101

73102
/**
74-
* Holds if `foreach` statement `fes` could be converted to an `.OfType()` call.
103+
* Holds if `foreach` statement `fes` can be converted to an `.OfType()` call.
75104
* That is, the loop variable is accessed only in the first statement of the
76-
* block, and the access is a cast with the `as` operator. The first statement
77-
* needs to be a `LocalVariableDeclStmt`.
105+
* block, the access is a cast with the `as` operator, and the first statement
106+
* is a local variable declaration statement `s`.
78107
*/
79-
predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
108+
predicate missedOfTypeOpportunity(ForeachStmtEnumerable fes, LocalVariableDeclStmt s) {
80109
s = firstStmt(fes) and
81110
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
82111
va = s.getAVariableDeclExpr().getAChildExpr*()
@@ -88,12 +117,12 @@ predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
88117
}
89118

90119
/**
91-
* Holds if `foreach` statement `fes` could be converted to a `.Select()` call.
120+
* Holds if `foreach` statement `fes` can be converted to a `.Select()` call.
92121
* That is, the loop variable is accessed only in the first statement of the
93-
* block, and the access is not a cast. The first statement needs to be a
94-
* `LocalVariableDeclStmt`.
122+
* block, the access is not a cast, and the first statement is a
123+
* local variable declaration statement `s`.
95124
*/
96-
predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
125+
predicate missedSelectOpportunity(ForeachStmtGenericEnumerable fes, LocalVariableDeclStmt s) {
97126
s = firstStmt(fes) and
98127
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
99128
va = s.getAVariableDeclExpr().getAChildExpr*()
@@ -107,7 +136,7 @@ predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
107136
* variable, and the body of the `if` is either a `continue` or there's nothing
108137
* else in the loop than the `if`.
109138
*/
110-
predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is) {
139+
predicate missedWhereOpportunity(ForeachStmtGenericEnumerable fes, IfStmt is) {
111140
// The very first thing the foreach loop does is test its iteration variable.
112141
is = firstStmt(fes) and
113142
exists(VariableAccess va |

csharp/ql/lib/semmle/code/csharp/Stmt.qll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,12 @@ class YieldReturnStmt extends YieldStmt {
861861
override string getAPrimaryQlClass() { result = "YieldReturnStmt" }
862862
}
863863

864+
bindingset[cfe1, cfe2]
865+
pragma[inline_late]
866+
private predicate sameCallable(ControlFlowElement cfe1, ControlFlowElement cfe2) {
867+
cfe1.getEnclosingCallable() = cfe2.getEnclosingCallable()
868+
}
869+
864870
/**
865871
* A `try` statement, for example
866872
*
@@ -947,8 +953,7 @@ class TryStmt extends Stmt, @try_stmt {
947953
mid = this.getATriedElement() and
948954
not mid instanceof TryStmt and
949955
result = mid.getAChild() and
950-
pragma[only_bind_into](mid.getEnclosingCallable()) =
951-
pragma[only_bind_into](result.getEnclosingCallable())
956+
sameCallable(mid, result)
952957
)
953958
}
954959
}

csharp/ql/src/Linq/MissedAllOpportunity.ql

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
* language-features
1111
*/
1212

13-
import csharp
1413
import Linq.Helpers
1514

1615
/*
@@ -31,7 +30,7 @@ import Linq.Helpers
3130
* bool allEven = lst.All(i => i % 2 == 0);
3231
*/
3332

34-
from ForeachStmt fes
33+
from ForeachStmtGenericEnumerable fes
3534
where missedAllOpportunity(fes)
3635
select fes,
3736
"This foreach loop looks as if it might be testing whether every sequence element satisfies a predicate - consider using '.All(...)'."

csharp/ql/src/Linq/MissedCastOpportunity.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import csharp
1414
import Linq.Helpers
1515

16-
from ForeachStmt fes, LocalVariableDeclStmt s
16+
from ForeachStmtEnumerable fes, LocalVariableDeclStmt s
1717
where missedCastOpportunity(fes, s)
1818
select fes,
1919
"This foreach loop immediately $@ - consider casting the sequence explicitly using '.Cast(...)'.",

csharp/ql/src/Linq/MissedOfTypeOpportunity.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import csharp
1414
import Linq.Helpers
1515

16-
from ForeachStmt fes, LocalVariableDeclStmt s
16+
from ForeachStmtEnumerable fes, LocalVariableDeclStmt s
1717
where missedOfTypeOpportunity(fes, s)
1818
select fes,
1919
"This foreach loop immediately uses 'as' to $@ - consider using '.OfType(...)' instead.", s,

csharp/ql/src/Linq/MissedSelectOpportunity.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ predicate oversized(LocalVariableDeclStmt s) {
2020
)
2121
}
2222

23-
from ForeachStmt fes, LocalVariableDeclStmt s
23+
from ForeachStmtGenericEnumerable fes, LocalVariableDeclStmt s
2424
where
2525
missedSelectOpportunity(fes, s) and
2626
not oversized(s)

0 commit comments

Comments
 (0)