Skip to content

Commit 2fd461e

Browse files
authored
Merge pull request github#5938 from MathiasVP/promote-access-of-memory-location-after-end-of-buffer-using-strncat
C++: Promote `cpp/access-memory-location-after-end-buffer-strncat` out of experimental
2 parents 1997f50 + 78cc8f0 commit 2fd461e

13 files changed

+127
-140
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm
2+
* The "Potentially unsafe call to strncat" query (cpp/unsafe-strncat) query has been improved to detect more cases of unsafe calls to `strncat`.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ strncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest
22

33
strncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest.
44
//Also fails if dest is a pointer and not an array.
5+
6+
strncat(dest, source, sizeof(dest) - strlen(dest)); // wrong: writes a zero byte past the `dest` buffer.
7+
8+
strncat(dest, source, sizeof(dest) - strlen(dest) - 1); // correct: reserves space for the zero byte.

cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,17 @@
44
<qhelp>
55
<overview>
66
<p>The standard library function <code>strncat</code> appends a source string to a target string.
7-
The third argument defines the maximum number of characters to append and should be less than or equal to the remaining space in the destination buffer. Calls of the form <code>strncat(dest, src, strlen(dest))</code> or <code>strncat(dest, src, sizeof(dest))</code> set the third argument to the entire size of the destination buffer. Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.</p>
7+
The third argument defines the maximum number of characters to append and should be less than or equal
8+
to the remaining space in the destination buffer.</p>
9+
10+
<p>Calls of the form <code>strncat(dest, src, strlen(dest))</code> or <code>strncat(dest, src, sizeof(dest))</code> set
11+
the third argument to the entire size of the destination buffer.
12+
Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty.</p>
13+
14+
<p>Similarly, calls of the form <code>strncat(dest, src, sizeof (dest) - strlen (dest))</code> allow one
15+
byte to be written ouside the <code>dest</code> buffer.</p>
16+
17+
<p>Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.</p>
818

919
</overview>
1020
<recommendation>
@@ -25,6 +35,10 @@ The third argument defines the maximum number of characters to append and should
2535
<li>
2636
M. Donaldson, <em>Inside the Buffer Overflow Attack: Mechanism, Method &amp; Prevention</em>. SANS Institute InfoSec Reading Room, 2002.
2737
</li>
38+
<li>
39+
CERT C Coding Standard:
40+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.
41+
</li>
2842

2943

3044
</references>
Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,68 @@
11
/**
22
* @name Potentially unsafe call to strncat
3-
* @description Calling 'strncat' with the size of the destination buffer
4-
* as the third argument may result in a buffer overflow.
3+
* @description Calling 'strncat' with an incorrect size argument may result in a buffer overflow.
54
* @kind problem
65
* @problem.severity warning
76
* @precision medium
87
* @id cpp/unsafe-strncat
98
* @tags reliability
109
* correctness
1110
* security
11+
* external/cwe/cwe-788
1212
* external/cwe/cwe-676
1313
* external/cwe/cwe-119
1414
* external/cwe/cwe-251
1515
*/
1616

1717
import cpp
1818
import Buffer
19+
import semmle.code.cpp.models.implementations.Strcat
20+
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
1921

20-
from FunctionCall fc, VariableAccess va1, VariableAccess va2
21-
where
22-
fc.getTarget().(Function).hasName("strncat") and
23-
va1 = fc.getArgument(0) and
24-
va2 = fc.getArgument(2).(BufferSizeExpr).getArg() and
25-
va1.getTarget() = va2.getTarget()
22+
/**
23+
* Holds if `call` is a call to `strncat` such that `sizeArg` and `destArg` are the size and
24+
* destination arguments, respectively.
25+
*/
26+
predicate interestringCallWithArgs(Call call, Expr sizeArg, Expr destArg) {
27+
exists(StrcatFunction strcat |
28+
strcat = call.getTarget() and
29+
sizeArg = call.getArgument(strcat.getParamSize()) and
30+
destArg = call.getArgument(strcat.getParamDest())
31+
)
32+
}
33+
34+
/**
35+
* Holds if `fc` is a call to `strncat` with size argument `sizeArg` and destination
36+
* argument `destArg`, and `destArg` is the size of the buffer pointed to by `destArg`.
37+
*/
38+
predicate case1(FunctionCall fc, Expr sizeArg, VariableAccess destArg) {
39+
interestringCallWithArgs(fc, sizeArg, destArg) and
40+
exists(VariableAccess va |
41+
va = sizeArg.(BufferSizeExpr).getArg() and
42+
destArg.getTarget() = va.getTarget()
43+
)
44+
}
45+
46+
/**
47+
* Holds if `fc` is a call to `strncat` with size argument `sizeArg` and destination
48+
* argument `destArg`, and `sizeArg` computes the value `sizeof (dest) - strlen (dest)`.
49+
*/
50+
predicate case2(FunctionCall fc, Expr sizeArg, VariableAccess destArg) {
51+
interestringCallWithArgs(fc, sizeArg, destArg) and
52+
exists(SubExpr sub, int n |
53+
// The destination buffer is an array of size n
54+
destArg.getUnspecifiedType().(ArrayType).getSize() = n and
55+
// The size argument is equivalent to a subtraction
56+
globalValueNumber(sizeArg).getAnExpr() = sub and
57+
// ... where the left side of the subtraction is the constant n
58+
globalValueNumber(sub.getLeftOperand()).getAnExpr().getValue().toInt() = n and
59+
// ... and the right side of the subtraction is a call to `strlen` where the argument is the
60+
// destination buffer.
61+
globalValueNumber(sub.getRightOperand()).getAnExpr().(StrlenCall).getStringExpr() =
62+
globalValueNumber(destArg).getAnExpr()
63+
)
64+
}
65+
66+
from FunctionCall fc, Expr sizeArg, Expr destArg
67+
where case1(fc, sizeArg, destArg) or case2(fc, sizeArg, destArg)
2668
select fc, "Potentially unsafe call to strncat."

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

Lines changed: 0 additions & 4 deletions
This file was deleted.

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

Lines changed: 0 additions & 32 deletions
This file was deleted.

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

Lines changed: 0 additions & 42 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
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. |
1+
| test.c:16:3:16:24 | ... = ... | potential unsafe or redundant assignment. |
2+
| test.c:17:3:17:40 | ... = ... | potential unsafe or redundant assignment. |
3+
| test.c:18:3:18:44 | ... = ... | potential unsafe or redundant assignment. |
4+
| test.c:19:3:19:44 | ... = ... | potential unsafe or redundant assignment. |
5+
| test.c:20:3:20:48 | ... = ... | potential unsafe or redundant assignment. |
6+
| test.c:21:3:21:48 | ... = ... | potential unsafe or redundant assignment. |
7+
| test.c:22:3:22:52 | ... = ... | potential unsafe or redundant assignment. |
8+
| test.c:23:3:23:50 | ... = ... | potential unsafe or redundant assignment. |
9+
| test.c:24:3:24:54 | ... = ... | potential unsafe or redundant assignment. |

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

Lines changed: 0 additions & 5 deletions
This file was deleted.

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

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)