Skip to content

Commit 6853f49

Browse files
authored
Merge pull request github#6794 from geoffw0/impropnullfp
C++: Improvements to cpp/improper-null-termination
2 parents 10739b1 + ac6acfb commit 6853f49

File tree

7 files changed

+116
-32
lines changed

7 files changed

+116
-32
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Several improvements made to the `NullTermination.qll` library and the 'Potential improper null termination' (cpp/improper-null-termination). These changes reduce the number of false positive results for this query and related query 'User-controlled data may not be null terminated' (cpp/user-controlled-null-termination-tainted).

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import cpp
22
private import semmle.code.cpp.models.interfaces.ArrayFunction
33
private import semmle.code.cpp.models.implementations.Strcat
4+
import semmle.code.cpp.dataflow.DataFlow
45

56
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
67
exists(StackVariable v0, Expr val |
@@ -45,22 +46,28 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
4546
ae.getRValue().getAChild*() = va
4647
)
4748
or
48-
// Function call: library function, varargs function, function
49-
// containing assembler code, or function where the relevant
50-
// parameter is potentially added a null terminator.
49+
// Function calls...
5150
exists(Call c, Function f, int i |
5251
e = c and
5352
f = c.getTarget() and
5453
not functionArgumentMustBeNullTerminated(f, i) and
5554
c.getAnArgumentSubExpr(i) = va
5655
|
57-
not f.hasEntryPoint() and not functionArgumentMustBeNullTerminated(f, i)
56+
// library function
57+
not f.hasEntryPoint()
5858
or
59+
// function where the relevant parameter is potentially added a null terminator
5960
mayAddNullTerminator(_, f.getParameter(i).getAnAccess())
6061
or
62+
// varargs function
6163
f.isVarargs() and i >= f.getNumberOfParameters()
6264
or
65+
// function containing assembler code
6366
exists(AsmStmt s | s.getEnclosingFunction() = f)
67+
or
68+
// function where the relevant parameter is returned (leaking it to be potentially null terminated elsewhere)
69+
DataFlow::localFlow(DataFlow::parameterNode(f.getParameter(i)),
70+
DataFlow::exprNode(any(ReturnStmt rs).getExpr()))
6471
)
6572
or
6673
// Call without target (e.g., function pointer call)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* @kind problem
66
* @id cpp/improper-null-termination
77
* @problem.severity warning
8+
* @precision medium
89
* @security-severity 7.8
910
* @tags security
1011
* external/cwe/cwe-170
@@ -53,6 +54,7 @@ class ImproperNullTerminationReachability extends StackVariableReachabilityWithR
5354
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
5455
exprDefinition(v, node, _) or
5556
mayAddNullTerminator(node, v.getAnAccess()) or
57+
node.(AddressOfExpr).getOperand() = v.getAnAccess() or // address taken
5658
isSinkActual(node, v) // only report first use
5759
}
5860
}

cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* @kind problem
66
* @id cpp/user-controlled-null-termination-tainted
77
* @problem.severity warning
8+
* @precision medium
89
* @security-severity 10.0
910
* @tags security
1011
* external/cwe/cwe-170
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1-
| test.cpp:25:10:25:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:22:8:22:14 | buffer1 | buffer1 |
2-
| test.cpp:26:10:26:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:23:8:23:14 | buffer2 | buffer2 |
3-
| test.cpp:39:10:39:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:35:8:35:14 | buffer2 | buffer2 |
4-
| test.cpp:59:10:59:13 | ptr1 | Variable $@ may not be null terminated. | test.cpp:56:9:56:12 | ptr1 | ptr1 |
5-
| test.cpp:69:10:69:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 |
6-
| test.cpp:70:10:70:12 | ptr | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 |
7-
| test.cpp:81:10:81:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 |
8-
| test.cpp:82:10:82:12 | ptr | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 |
9-
| test.cpp:93:10:93:15 | buffer | Variable $@ may not be null terminated. | test.cpp:86:8:86:13 | buffer | buffer |
10-
| test.cpp:116:10:116:15 | buffer | Variable $@ may not be null terminated. | test.cpp:109:8:109:13 | buffer | buffer |
11-
| test.cpp:130:14:130:19 | buffer | Variable $@ may not be null terminated. | test.cpp:127:7:127:12 | buffer | buffer |
12-
| test.cpp:139:10:139:15 | buffer | Variable $@ may not be null terminated. | test.cpp:136:8:136:13 | buffer | buffer |
13-
| test.cpp:147:14:147:19 | buffer | Variable $@ may not be null terminated. | test.cpp:143:8:143:13 | buffer | buffer |
14-
| test.cpp:182:10:182:15 | buffer | Variable $@ may not be null terminated. | test.cpp:178:8:178:13 | buffer | buffer |
15-
| test.cpp:234:10:234:15 | buffer | Variable $@ may not be null terminated. | test.cpp:232:8:232:13 | buffer | buffer |
16-
| test.cpp:262:10:262:15 | buffer | Variable $@ may not be null terminated. | test.cpp:259:8:259:13 | buffer | buffer |
17-
| test.cpp:283:10:283:15 | buffer | Variable $@ may not be null terminated. | test.cpp:280:8:280:13 | buffer | buffer |
18-
| test.cpp:300:10:300:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:295:8:295:14 | buffer2 | buffer2 |
19-
| test.cpp:312:10:312:15 | buffer | Variable $@ may not be null terminated. | test.cpp:308:8:308:13 | buffer | buffer |
20-
| test.cpp:327:18:327:23 | buffer | Variable $@ may not be null terminated. | test.cpp:326:8:326:13 | buffer | buffer |
21-
| test.cpp:346:11:346:16 | buffer | Variable $@ may not be null terminated. | test.cpp:341:8:341:13 | buffer | buffer |
1+
| test.cpp:26:10:26:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:23:8:23:14 | buffer1 | buffer1 |
2+
| test.cpp:27:10:27:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:24:8:24:14 | buffer2 | buffer2 |
3+
| test.cpp:40:10:40:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:36:8:36:14 | buffer2 | buffer2 |
4+
| test.cpp:60:10:60:13 | ptr1 | Variable $@ may not be null terminated. | test.cpp:57:9:57:12 | ptr1 | ptr1 |
5+
| test.cpp:70:10:70:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer1 | buffer1 |
6+
| test.cpp:71:10:71:12 | ptr | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer1 | buffer1 |
7+
| test.cpp:82:10:82:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:66:8:66:14 | buffer2 | buffer2 |
8+
| test.cpp:83:10:83:12 | ptr | Variable $@ may not be null terminated. | test.cpp:66:8:66:14 | buffer2 | buffer2 |
9+
| test.cpp:94:10:94:15 | buffer | Variable $@ may not be null terminated. | test.cpp:87:8:87:13 | buffer | buffer |
10+
| test.cpp:117:10:117:15 | buffer | Variable $@ may not be null terminated. | test.cpp:110:8:110:13 | buffer | buffer |
11+
| test.cpp:131:14:131:19 | buffer | Variable $@ may not be null terminated. | test.cpp:128:7:128:12 | buffer | buffer |
12+
| test.cpp:140:10:140:15 | buffer | Variable $@ may not be null terminated. | test.cpp:137:8:137:13 | buffer | buffer |
13+
| test.cpp:148:14:148:19 | buffer | Variable $@ may not be null terminated. | test.cpp:144:8:144:13 | buffer | buffer |
14+
| test.cpp:183:10:183:15 | buffer | Variable $@ may not be null terminated. | test.cpp:179:8:179:13 | buffer | buffer |
15+
| test.cpp:236:10:236:15 | buffer | Variable $@ may not be null terminated. | test.cpp:234:8:234:13 | buffer | buffer |
16+
| test.cpp:264:10:264:15 | buffer | Variable $@ may not be null terminated. | test.cpp:261:8:261:13 | buffer | buffer |
17+
| test.cpp:285:10:285:15 | buffer | Variable $@ may not be null terminated. | test.cpp:282:8:282:13 | buffer | buffer |
18+
| test.cpp:302:10:302:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:297:8:297:14 | buffer2 | buffer2 |
19+
| test.cpp:314:10:314:15 | buffer | Variable $@ may not be null terminated. | test.cpp:310:8:310:13 | buffer | buffer |
20+
| test.cpp:336:18:336:23 | buffer | Variable $@ may not be null terminated. | test.cpp:335:8:335:13 | buffer | buffer |
2221
| test.cpp:355:11:355:16 | buffer | Variable $@ may not be null terminated. | test.cpp:350:8:350:13 | buffer | buffer |
23-
| test.cpp:365:19:365:25 | buffer2 | Variable $@ may not be null terminated. | test.cpp:363:8:363:14 | buffer2 | buffer2 |
24-
| test.cpp:392:17:392:22 | buffer | Variable $@ may not be null terminated. | test.cpp:390:8:390:13 | buffer | buffer |
25-
| test.cpp:398:18:398:23 | buffer | Variable $@ may not be null terminated. | test.cpp:396:8:396:13 | buffer | buffer |
26-
| test.cpp:444:10:444:15 | buffer | Variable $@ may not be null terminated. | test.cpp:442:8:442:13 | buffer | buffer |
27-
| test.cpp:450:16:450:21 | buffer | Variable $@ may not be null terminated. | test.cpp:448:8:448:13 | buffer | buffer |
22+
| test.cpp:364:11:364:16 | buffer | Variable $@ may not be null terminated. | test.cpp:359:8:359:13 | buffer | buffer |
23+
| test.cpp:392:11:392:16 | buffer | Variable $@ may not be null terminated. | test.cpp:381:8:381:13 | buffer | buffer |
24+
| test.cpp:410:11:410:16 | buffer | Variable $@ may not be null terminated. | test.cpp:397:8:397:13 | buffer | buffer |
25+
| test.cpp:421:19:421:25 | buffer2 | Variable $@ may not be null terminated. | test.cpp:419:8:419:14 | buffer2 | buffer2 |
26+
| test.cpp:448:17:448:22 | buffer | Variable $@ may not be null terminated. | test.cpp:446:8:446:13 | buffer | buffer |
27+
| test.cpp:454:18:454:23 | buffer | Variable $@ may not be null terminated. | test.cpp:452:8:452:13 | buffer | buffer |
28+
| test.cpp:513:10:513:15 | buffer | Variable $@ may not be null terminated. | test.cpp:511:8:511:13 | buffer | buffer |
29+
| test.cpp:519:16:519:21 | buffer | Variable $@ may not be null terminated. | test.cpp:517:8:517:13 | buffer | buffer |
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
| test.cpp:410:10:410:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:409:18:409:23 | buffer | User-provided value |
2-
| test.cpp:425:10:425:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:424:9:424:14 | buffer | User-provided value |
1+
| test.cpp:466:10:466:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:465:18:465:23 | buffer | User-provided value |
2+
| test.cpp:481:10:481:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:480:9:480:14 | buffer | User-provided value |

cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ size_t strlen(const char *s);
66
char *strcpy(char *s1, const char *s2);
77
char *strcat(char *s1, const char *s2);
88
char *strdup(const char *s1);
9+
long int strtol(const char* nptr, char** endptr, int base);
910
void *malloc(size_t size);
1011
void *memset(void *s, int c, size_t n);
1112
void *memcpy(void *s1, const void *s2, size_t n);
@@ -225,6 +226,7 @@ void test_readlink(int fd, const char *path, size_t sz)
225226
void doNothing(char *data) { };
226227
void doNothing2(const char *data);
227228
void clearBuffer(char *data, size_t size);
229+
char *id(char *data) { return data; }
228230

229231
void test_strcat()
230232
{
@@ -318,6 +320,13 @@ void test_strcat()
318320
clearBuffer(buffer, 1024);
319321
strcat(buffer, "content"); // GOOD
320322
}
323+
324+
{
325+
char buffer[1024];
326+
327+
clearBuffer(id(buffer), 1024);
328+
strcat(buffer, "content"); // GOOD
329+
}
321330
}
322331

323332
void test_strlen(bool cond1, bool cond2)
@@ -354,6 +363,53 @@ void test_strlen(bool cond1, bool cond2)
354363
if (cond2)
355364
strlen(buffer); // BAD
356365
}
366+
367+
{
368+
char buffer[1024];
369+
370+
if (cond1)
371+
{
372+
buffer[0] = 0;
373+
} else {
374+
buffer[0] = 0;
375+
}
376+
377+
strlen(buffer); // GOOD
378+
}
379+
380+
{
381+
char buffer[1024];
382+
int init = 0;
383+
384+
if (cond1)
385+
{
386+
buffer[0] = 0;
387+
init = 1;
388+
}
389+
390+
if (init != 0)
391+
{
392+
strlen(buffer); // GOOD [FALSE POSITIVE]
393+
}
394+
}
395+
396+
{
397+
char buffer[1024];
398+
int init = 0;
399+
400+
if (cond1)
401+
{
402+
buffer[0] = 0;
403+
init = 1;
404+
}
405+
406+
if (init == 0)
407+
{
408+
// ...
409+
} else {
410+
strlen(buffer); // GOOD [FALSE POSITIVE]
411+
}
412+
}
357413
}
358414

359415
void test_strcpy()
@@ -434,6 +490,19 @@ void test_read_fread(int read_src, FILE *s)
434490
}
435491
}
436492

493+
void test_strtol()
494+
{
495+
{
496+
char buffer[100];
497+
char *after_ptr;
498+
long int num;
499+
500+
strcpy(buffer, "123abc");
501+
num = strtol("123abc", &after_ptr, 10);
502+
strlen(after_ptr); // GOOD
503+
}
504+
}
505+
437506
int printf(const char *format, ...);
438507

439508
void test_printf(char *str)
@@ -466,3 +535,4 @@ void test_printf(char *str)
466535
printf("%s", copied_str); // GOOD
467536
}
468537
}
538+

0 commit comments

Comments
 (0)