Skip to content

Commit 8dcf792

Browse files
authored
Merge pull request github#6760 from andersfugmann/relax_memberMayBeVarSize
Increase precision to high for cpp/static-buffer-overflow
2 parents d09c3bf + ba98c0c commit 8dcf792

File tree

10 files changed

+40
-72
lines changed

10 files changed

+40
-72
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* Increase precision to high for the "Static buffer overflow" query
3+
(`cpp/static-buffer-overflow`). This means the query is run and displayed by default on Code Scanning and LGTM.

cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,11 @@ import semmle.code.cpp.dataflow.DataFlow
1010
* char data[1]; // v
1111
* };
1212
* ```
13-
* This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`.
14-
* In addition, if the size of the structure is taken, there must be at least one instance
15-
* where a `c` pointer is allocated with additional space.
16-
* For example, holds for `c` if it occurs as
17-
* ```
18-
* malloc(sizeof(c) + 100 * sizeof(char))
19-
* ```
20-
* but not if it only ever occurs as
21-
* ```
22-
* malloc(sizeof(c))
23-
* ```
13+
* This requires that `v` is an array of size 0 or 1.
2414
*/
2515
predicate memberMayBeVarSize(Class c, MemberVariable v) {
26-
exists(int i |
27-
// `v` is the last field in `c`
28-
i = max(int j | c.getCanonicalMember(j) instanceof Field | j) and
29-
v = c.getCanonicalMember(i) and
30-
// v is an array of size at most 1
31-
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1 and
32-
not c instanceof Union
33-
) and
34-
// If the size is taken, then arithmetic is performed on the result at least once
35-
(
36-
// `sizeof(c)` is not taken
37-
not exists(SizeofOperator so |
38-
so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or
39-
so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c
40-
)
41-
or
42-
// or `sizeof(c)` is taken
43-
exists(SizeofOperator so |
44-
so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or
45-
so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c
46-
|
47-
// and arithmetic is performed on the result
48-
so.getParent*() instanceof AddExpr
49-
)
50-
)
16+
c = v.getDeclaringType() and
17+
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
5118
}
5219

5320
/**
@@ -60,10 +27,6 @@ int getBufferSize(Expr bufferExpr, Element why) {
6027
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
6128
why = bufferVar and
6229
not memberMayBeVarSize(_, bufferVar) and
63-
not exists(Union bufferType |
64-
bufferType.getAMemberVariable() = why and
65-
bufferVar.getUnspecifiedType().(ArrayType).getSize() <= 1
66-
) and
6730
not result = 0 // zero sized arrays are likely to have special usage, for example
6831
or
6932
// behaving a bit like a 'union' overlapping other fields.
@@ -85,13 +48,6 @@ int getBufferSize(Expr bufferExpr, Element why) {
8548
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
8649
result = getBufferSize(parentPtr, _) + bufferVar.getType().getSize() - parentClass.getSize()
8750
)
88-
or
89-
exists(Union bufferType |
90-
bufferType.getAMemberVariable() = why and
91-
why = bufferVar and
92-
bufferVar.getUnspecifiedType().(ArrayType).getSize() <= 1 and
93-
result = bufferType.getSize()
94-
)
9551
)
9652
or
9753
// buffer is a fixed size dynamic allocation

cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,6 +1586,15 @@ private module SimpleRangeAnalysisCached {
15861586
result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)])
15871587
}
15881588

1589+
/** Holds if the upper bound of `expr` may have been widened. This means the the upper bound is in practice likely to be overly wide. */
1590+
cached
1591+
predicate upperBoundMayBeWidened(Expr e) {
1592+
isRecursiveExpr(e) and
1593+
// Widening is not a problem if the post-analysis in `getGuardedUpperBound` has overridden the widening.
1594+
// Note that the RHS of `<` may be multi-valued.
1595+
not getGuardedUpperBound(e) < getTruncatedUpperBounds(e)
1596+
}
1597+
15891598
/**
15901599
* Holds if `expr` has a provably empty range. For example:
15911600
*

cpp/ql/src/Critical/OverflowStatic.ql

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @kind problem
66
* @problem.severity warning
77
* @security-severity 9.3
8-
* @precision medium
8+
* @precision high
99
* @id cpp/static-buffer-overflow
1010
* @tags reliability
1111
* security
@@ -55,6 +55,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
5555
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
5656
// Ensure that we don't have an upper bound on the array index that's less than the buffer size.
5757
not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and
58+
// The upper bounds analysis must not have been widended
59+
not upperBoundMayBeWidened(bufaccess.getArrayOffset().getFullyConverted()) and
5860
msg =
5961
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
6062
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
@@ -130,11 +132,13 @@ predicate outOfBounds(BufferAccess bufaccess, string msg) {
130132
(
131133
access > size
132134
or
133-
access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand())
135+
access = size and
136+
not exists(AddressOfExpr addof | bufaccess = addof.getOperand()) and
137+
not exists(BuiltInOperationBuiltInOffsetOf offsetof | offsetof.getAChild() = bufaccess)
134138
) and
135139
msg =
136140
"Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf + "[" +
137-
access.toString() + "]' is accessed here."
141+
access.toString() + "]' may be accessed here."
138142
)
139143
}
140144

cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@
55
| test2.c:33:26:33:27 | 46 | Potential buffer-overflow: 'buffer' has size 40 not 46. |
66
| test2.c:34:22:34:23 | 47 | Potential buffer-overflow: 'buffer' has size 40 not 47. |
77
| test2.c:35:23:35:24 | 48 | Potential buffer-overflow: 'buffer' has size 40 not 48. |
8-
| test.c:14:9:14:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[5]' is accessed here. |
9-
| test.c:15:9:15:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[6]' is accessed here. |
10-
| test.c:20:9:20:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[5]' is accessed here. |
11-
| test.c:21:9:21:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[6]' is accessed here. |
12-
| test.c:47:3:47:18 | access to array | Potential buffer-overflow: 'ptr' has size 8 but 'ptr[8]' is accessed here. |
13-
| test.c:54:3:54:26 | access to array | Potential buffer-overflow: 'ptr' has size 8 but 'ptr[8]' is accessed here. |
14-
| test.c:61:3:61:18 | access to array | Potential buffer-overflow: 'ptr' has size 8 but 'ptr[8]' is accessed here. |
15-
| test.c:72:3:72:11 | access to array | Potential buffer-overflow: 'buf' has size 1 but 'buf[1]' is accessed here. |
8+
| test.c:14:9:14:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[5]' may be accessed here. |
9+
| test.c:15:9:15:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[6]' may be accessed here. |
10+
| test.c:20:9:20:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[5]' may be accessed here. |
11+
| test.c:21:9:21:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[6]' may be accessed here. |
1612
| test.cpp:19:3:19:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer1' has 3 elements. |
1713
| test.cpp:20:3:20:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer2' has 3 elements. |
1814
| test.cpp:24:27:24:27 | 4 | Potential buffer-overflow: 'buffer1' has size 3 not 4. |

cpp/ql/test/query-tests/Critical/OverflowStatic/test.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,21 @@ void union_test() {
4444
union u u;
4545
u.ptr[0] = 0; // GOOD
4646
u.ptr[sizeof(u)-1] = 0; // GOOD
47-
u.ptr[sizeof(u)] = 0; // BAD
47+
u.ptr[sizeof(u)] = 0; // BAD [NOT DETECTED]
4848
}
4949

5050
void test_struct_union() {
5151
struct { union u u; } v;
5252
v.u.ptr[0] = 0; // GOOD
5353
v.u.ptr[sizeof(union u)-1] = 0; // GOOD
54-
v.u.ptr[sizeof(union u)] = 0; // BAD
54+
v.u.ptr[sizeof(union u)] = 0; // BAD [NOT DETECTED]
5555
}
5656

5757
void union_test2() {
5858
union { char ptr[1]; unsigned long value; } u;
5959
u.ptr[0] = 0; // GOOD
6060
u.ptr[sizeof(u)-1] = 0; // GOOD
61-
u.ptr[sizeof(u)] = 0; // BAD
61+
u.ptr[sizeof(u)] = 0; // BAD [NOT DETECTED]
6262
}
6363

6464
typedef struct {
@@ -69,5 +69,5 @@ typedef struct {
6969
void test_alloc() {
7070
// Special case of taking sizeof without any addition or multiplications
7171
var_buf *b = malloc(sizeof(var_buf));
72-
b->buf[1] = 0; // BAD
72+
b->buf[1] = 0; // BAD [NOT DETECTED]
7373
}

cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,9 @@
7272
| unions.cpp:30:2:30:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:15:7:15:11 | small | destination buffer |
7373
| unions.cpp:34:2:34:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:16:7:16:11 | large | destination buffer |
7474
| unions.cpp:34:2:34:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:34:14:34:18 | large | destination buffer |
75-
| var_size_struct.cpp:54:5:54:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:32:8:32:10 | str | array |
76-
| var_size_struct.cpp:55:5:55:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:38:8:38:10 | str | array |
7775
| var_size_struct.cpp:71:3:71:8 | call to memset | This 'memset' operation accesses 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer |
7876
| var_size_struct.cpp:73:3:73:9 | call to strncpy | This 'strncpy' operation may access 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer |
7977
| var_size_struct.cpp:87:3:87:19 | access to array | This array indexing operation accesses byte offset 67 but the $@ is only 64 bytes. | var_size_struct.cpp:78:7:78:14 | elements | array |
8078
| var_size_struct.cpp:99:3:99:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
8179
| var_size_struct.cpp:101:3:101:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
8280
| var_size_struct.cpp:103:3:103:9 | call to strncpy | This 'strncpy' operation may access 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
83-
| var_size_struct.cpp:169:3:169:8 | call to memset | This 'memset' operation accesses 100 bytes but the $@ is only 1 byte. | var_size_struct.cpp:125:17:125:19 | arr | destination buffer |

cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
| tests.cpp:163:3:163:11 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. |
44
| tests.cpp:164:8:164:16 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. |
55
| tests.cpp:245:42:245:42 | 6 | Potential buffer-overflow: 'global_array_5' has size 5 not 6. |
6-
| tests.cpp:349:2:349:14 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. |
7-
| tests.cpp:350:17:350:29 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. |
8-
| var_size_struct.cpp:54:5:54:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. |
9-
| var_size_struct.cpp:55:5:55:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. |
6+
| tests.cpp:349:2:349:14 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' may be accessed here. |
7+
| tests.cpp:350:17:350:29 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' may be accessed here. |
108
| var_size_struct.cpp:103:39:103:41 | 129 | Potential buffer-overflow: 'str' has size 128 not 129. |

cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ void testVarString(int n) {
5151
s1->str[1] = '?'; // GOOD
5252
s2->str[1] = '?'; // GOOD
5353
s3->str[1] = '?'; // GOOD
54-
s4->str[1] = '?'; // BAD
55-
s5->str[1] = '?'; // BAD
54+
s4->str[1] = '?'; // BAD [NOT DETECTED]
55+
s5->str[1] = '?'; // BAD [NOT DETECTED]
5656
}
5757
}
5858

@@ -166,7 +166,7 @@ void useVarStruct34(varStruct5 *vs5) {
166166

167167
void testVarStruct34(varStruct3 *vs3, varStruct4 *vs4, varStruct5 *vs5, varStruct6 *vs6, varStruct7 *vs7, varStruct8 *vs8, varStruct9 *vs9) {
168168
memset(vs3->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
169-
memset(vs4->arr, 'x', 100); // BAD: it's not variable size, so this is a buffer overflow
169+
memset(vs4->arr, 'x', 100); // BAD: [NOT DETECTED] it's not variable size, so this is a buffer overflow
170170
memset(vs5->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
171171
memset(vs6->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
172172
memset(vs7->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
| var_size_struct.cpp:13:8:13:17 | VarString1 | var_size_struct.cpp:15:8:15:10 | str |
22
| var_size_struct.cpp:18:8:18:17 | VarString2 | var_size_struct.cpp:20:8:20:10 | str |
33
| var_size_struct.cpp:24:8:24:17 | VarString3 | var_size_struct.cpp:26:8:26:10 | str |
4+
| var_size_struct.cpp:30:8:30:17 | VarString4 | var_size_struct.cpp:32:8:32:10 | str |
5+
| var_size_struct.cpp:36:8:36:17 | VarString5 | var_size_struct.cpp:38:8:38:10 | str |
46
| var_size_struct.cpp:36:8:36:17 | VarString5 | var_size_struct.cpp:39:8:39:11 | str2 |
57
| var_size_struct.cpp:61:8:61:17 | varStruct1 | var_size_struct.cpp:63:8:63:11 | data |
68
| var_size_struct.cpp:76:8:76:17 | varStruct2 | var_size_struct.cpp:78:7:78:14 | elements |
9+
| var_size_struct.cpp:106:8:106:20 | notVarStruct2 | var_size_struct.cpp:107:8:107:10 | str |
710
| var_size_struct.cpp:119:8:119:17 | varStruct3 | var_size_struct.cpp:121:17:121:19 | arr |
11+
| var_size_struct.cpp:123:8:123:17 | varStruct4 | var_size_struct.cpp:125:17:125:19 | arr |
812
| var_size_struct.cpp:127:8:127:17 | varStruct5 | var_size_struct.cpp:129:17:129:19 | arr |
913
| var_size_struct.cpp:131:8:131:17 | varStruct6 | var_size_struct.cpp:133:17:133:19 | arr |
1014
| var_size_struct.cpp:135:8:135:17 | varStruct7 | var_size_struct.cpp:137:17:137:19 | arr |
1115
| var_size_struct.cpp:139:8:139:17 | varStruct8 | var_size_struct.cpp:141:9:141:11 | arr |
1216
| var_size_struct.cpp:143:8:143:17 | varStruct9 | var_size_struct.cpp:145:17:145:19 | arr |
17+
| var_size_struct.cpp:181:8:181:18 | PseudoUnion | var_size_struct.cpp:183:7:183:10 | data |

0 commit comments

Comments
 (0)