Skip to content

Commit 9686324

Browse files
committed
Bring over documentation for new rules.
1 parent df6c9d4 commit 9686324

43 files changed

Lines changed: 1360 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <stdio.h>
2+
int main(int argc, char** argv) {
3+
for(int i = 1; i < argc; ++i) {
4+
printf(argv[i]);
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <stdio.h>
2+
int main(int argc, char** argv) {
3+
for(int i = 1; i < argc; ++i) {
4+
printf("%s", argv[i]);
5+
}
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void log_with_timestamp(const char* message) {
2+
struct tm now;
3+
time(&now);
4+
printf("[%s] ", asctime(now));
5+
printf(message);
6+
}
7+
8+
int main(int argc, char** argv) {
9+
log_with_timestamp("Application is starting...\n");
10+
/* ... */
11+
log_with_timestamp("Application is closing...\n");
12+
return 0;
13+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
void log_with_timestamp(const char* message, ...) {
2+
va_list args;
3+
va_start(args, message);
4+
struct tm now;
5+
time(&now);
6+
printf("[%s] ", asctime(now));
7+
vprintf(message, args);
8+
va_end(args);
9+
}
10+
11+
int main(int argc, char** argv) {
12+
log_with_timestamp("%s is starting...\n", argv[0]);
13+
/* ... */
14+
log_with_timestamp("%s is closing...\n", argv[0]);
15+
return 0;
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
void log_with_timestamp(const char* message) {
2+
struct tm now;
3+
time(&now);
4+
printf("[%s] %s", asctime(now), message);
5+
}
6+
7+
int main(int argc, char** argv) {
8+
log_with_timestamp("Application is starting...\n");
9+
/* ... */
10+
log_with_timestamp("Application is closing...\n");
11+
return 0;
12+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Non-constant format string
2+
The `printf` function, related functions like `sprintf` and `fprintf`, and other functions built atop `vprintf` all accept a format string as one of their arguments. When such format strings are literal constants, it is easy for the programmer (and static analysis tools) to verify that the format specifiers (such as `%s` and `%02x`) in the format string are compatible with the trailing arguments of the function call. When such format strings are not literal constants, it is more difficult to maintain the program: programmers (and static analysis tools) must perform non-local data-flow analysis to deduce what values the format string argument might take.
3+
4+
5+
## Recommendation
6+
If the argument passed as a format string is meant to be a plain string rather than a format string, then pass `%s` as the format string, and pass the original argument as the sole trailing argument.
7+
8+
If the argument passed as a format string is a parameter to the enclosing function, then consider redesigning the enclosing function's API to be less brittle.
9+
10+
11+
## Example
12+
The following program is meant to echo its command line arguments:
13+
14+
15+
```c
16+
#include <stdio.h>
17+
int main(int argc, char** argv) {
18+
for(int i = 1; i < argc; ++i) {
19+
printf(argv[i]);
20+
}
21+
}
22+
```
23+
The above program behaves as expected in most cases, but breaks when one of its command line arguments contains a percent character. In such cases, the behavior of the program is undefined: it might echo garbage, it might crash, or it might give a malicious attacker root access. One way of addressing the problem is to use a constant `%s` format string, as in the following program:
24+
25+
26+
```c
27+
#include <stdio.h>
28+
int main(int argc, char** argv) {
29+
for(int i = 1; i < argc; ++i) {
30+
printf("%s", argv[i]);
31+
}
32+
}
33+
```
34+
35+
## Example
36+
The following program defines a `log_with_timestamp` function:
37+
38+
39+
```c
40+
void log_with_timestamp(const char* message) {
41+
struct tm now;
42+
time(&now);
43+
printf("[%s] ", asctime(now));
44+
printf(message);
45+
}
46+
47+
int main(int argc, char** argv) {
48+
log_with_timestamp("Application is starting...\n");
49+
/* ... */
50+
log_with_timestamp("Application is closing...\n");
51+
return 0;
52+
}
53+
```
54+
In the code that is visible, the reader can verify that `log_with_timestamp` is never called with a log message containing a percent character, but even if all current calls are correct, this presents an ongoing maintenance burden to ensure that newly-introduced calls don't contain percent characters. As in the previous example, one solution is to make the log message a trailing argument of the function call:
55+
56+
57+
```c
58+
void log_with_timestamp(const char* message) {
59+
struct tm now;
60+
time(&now);
61+
printf("[%s] %s", asctime(now), message);
62+
}
63+
64+
int main(int argc, char** argv) {
65+
log_with_timestamp("Application is starting...\n");
66+
/* ... */
67+
log_with_timestamp("Application is closing...\n");
68+
return 0;
69+
}
70+
```
71+
An alternative solution is to allow `log_with_timestamp` to accept format arguments:
72+
73+
74+
```c
75+
void log_with_timestamp(const char* message, ...) {
76+
va_list args;
77+
va_start(args, message);
78+
struct tm now;
79+
time(&now);
80+
printf("[%s] ", asctime(now));
81+
vprintf(message, args);
82+
va_end(args);
83+
}
84+
85+
int main(int argc, char** argv) {
86+
log_with_timestamp("%s is starting...\n", argv[0]);
87+
/* ... */
88+
log_with_timestamp("%s is closing...\n", argv[0]);
89+
return 0;
90+
}
91+
```
92+
In this formulation, the non-constant format string to `printf` has been replaced with a non-constant format string to `vprintf`. The analysis will no longer consider the body of `log_with_timestamp` to be a problem, and will instead check that every call to `log_with_timestamp` passes a constant format string.
93+
94+
95+
## References
96+
* CERT C Coding Standard: [FIO30-C. Exclude user input from format strings](https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings).
97+
* M. Howard, D. Leblanc, J. Viega, *19 Deadly Sins of Software Security: Programming Flaws and How to Fix Them*.
98+
* Common Weakness Enumeration: [CWE-134](https://cwe.mitre.org/data/definitions/134.html).
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>The <code>printf</code> function, related functions like <code>sprintf</code> and <code>fprintf</code>,
7+
and other functions built atop <code>vprintf</code> all accept a format string as one of their arguments.
8+
When such format strings are literal constants, it is easy for the programmer (and static analysis tools)
9+
to verify that the format specifiers (such as <code>%s</code> and <code>%02x</code>) in the format string
10+
are compatible with the trailing arguments of the function call. When such format strings are not literal
11+
constants, it is more difficult to maintain the program: programmers (and static analysis tools) must
12+
perform non-local data-flow analysis to deduce what values the format string argument might take.</p>
13+
14+
</overview>
15+
<recommendation>
16+
<p>If the argument passed as a format string is meant to be a plain string rather than a format string,
17+
then pass <code>%s</code> as the format string, and pass the original argument as the sole trailing
18+
argument.</p>
19+
20+
<p>If the argument passed as a format string is a parameter to the enclosing function, then consider
21+
redesigning the enclosing function's API to be less brittle.</p>
22+
23+
</recommendation>
24+
<example>
25+
<p>The following program is meant to echo its command line arguments:</p>
26+
<sample src="NonConstantFormat-1-bad.c" />
27+
<p>The above program behaves as expected in most cases, but breaks when one of its command line arguments
28+
contains a percent character. In such cases, the behavior of the program is undefined: it might echo
29+
garbage, it might crash, or it might give a malicious attacker root access. One way of addressing
30+
the problem is to use a constant <code>%s</code> format string, as in the following program:</p>
31+
<sample src="NonConstantFormat-1-good.c" />
32+
33+
</example>
34+
<example>
35+
<p>The following program defines a <code>log_with_timestamp</code> function:</p>
36+
<sample src="NonConstantFormat-2-bad.c" />
37+
<p>In the code that is visible, the reader can verify that <code>log_with_timestamp</code> is never called
38+
with a log message containing a percent character, but even if all current calls are correct, this presents
39+
an ongoing maintenance burden to ensure that newly-introduced calls don't contain percent characters. As
40+
in the previous example, one solution is to make the log message a trailing argument of the function call:</p>
41+
<sample src="NonConstantFormat-2-ok.c" />
42+
<p>An alternative solution is to allow <code>log_with_timestamp</code> to accept format arguments:</p>
43+
<sample src="NonConstantFormat-2-good.c" />
44+
<p>In this formulation, the non-constant format string to <code>printf</code> has been replaced with
45+
a non-constant format string to <code>vprintf</code>. The analysis will no longer consider the body of
46+
<code>log_with_timestamp</code> to be a problem, and will instead check that every call to
47+
<code>log_with_timestamp</code> passes a constant format string.</p>
48+
49+
</example>
50+
<references>
51+
52+
53+
<li>CERT C Coding
54+
Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings">FIO30-C. Exclude user input from format strings</a>.</li>
55+
<li>M. Howard, D. Leblanc, J. Viega, <i>19 Deadly Sins of Software Security: Programming Flaws and How to Fix Them</i>.</li>
56+
57+
58+
</references>
59+
</qhelp>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Potential improper null termination
2+
Built-in C string functions such as `strcat` require that their input string arguments are null terminated. If the input string arguments are not null terminated, these functions will read/write beyond the length of the buffer containing the string, resulting in either buffer over-read or buffer overflow, respectively.
3+
4+
5+
## Recommendation
6+
Review the code and consider whether the variable that holds the string should have an initializer or whether some path through the program fails to null terminate the string.
7+
8+
9+
## Example
10+
The destination variable `dest` used in the call to `strcat` does not (necessarily) contain a null terminator. Consequently, the call to `strcat` may result in a buffer overflow.
11+
12+
13+
```cpp
14+
char source[100];
15+
memset(source, 'A', 100-1);
16+
source[100-1] = '\0'; // null terminate source
17+
18+
char dest[200];
19+
memset(dest, 'B', 100-1);
20+
21+
strcat(dest, source);
22+
```
23+
In the revised example, `dest` is properly null terminated before the the call to `strcat`.
24+
25+
26+
```cpp
27+
char source[100];
28+
memset(source, 'A', 100-1);
29+
source[100-1] = '\0'; // null terminate source
30+
31+
char dest[200];
32+
memset(dest, 'B', 100-1);
33+
dest[100-1] = '\0'; // null terminate destination
34+
35+
strcat(dest, source);
36+
```
37+
38+
## References
39+
* B. Chess and J. West, *Secure Programming with Static Analysis*, 6.2 Maintaining the Null Terminator. Addison-Wesley. 2007.
40+
* Linux Programmer's Manual: [STRCAT(3)](http://man7.org/linux/man-pages/man3/strncat.3.html).
41+
* Common Weakness Enumeration: [CWE-170](https://cwe.mitre.org/data/definitions/170.html).
42+
* Common Weakness Enumeration: [CWE-665](https://cwe.mitre.org/data/definitions/665.html).
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
7+
<overview>
8+
<p> Built-in C string functions such as <code>strcat</code> require that their
9+
input string arguments are null terminated. If the input string arguments are
10+
not null terminated, these functions will read/write beyond the length of the
11+
buffer containing the string, resulting in either buffer over-read or buffer
12+
overflow, respectively.
13+
</p>
14+
15+
</overview>
16+
<recommendation>
17+
18+
<p>
19+
Review the code and consider whether the variable that holds the string should have
20+
an initializer or whether some path through the program fails to null terminate the
21+
string.
22+
</p>
23+
24+
</recommendation>
25+
<example>
26+
<p>The destination variable <code>dest</code> used in the call to <code>strcat</code>
27+
does not (necessarily) contain a null terminator. Consequently, the call to <code>strcat</code>
28+
may result in a buffer overflow.
29+
</p>
30+
31+
<sample src="ImproperNullTerminationBad.cpp" />
32+
33+
<p>In the revised example, <code>dest</code> is properly null terminated before the
34+
the call to <code>strcat</code>.
35+
</p>
36+
37+
<sample src="ImproperNullTerminationGood.cpp" />
38+
39+
40+
</example>
41+
<references>
42+
43+
<li>B. Chess and J. West, <em>Secure Programming with Static Analysis</em>, 6.2 Maintaining the Null Terminator. Addison-Wesley. 2007.</li>
44+
<li>Linux Programmer's Manual: <a href="http://man7.org/linux/man-pages/man3/strncat.3.html">STRCAT(3)</a>.</li>
45+
46+
</references>
47+
</qhelp>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
char source[100];
2+
memset(source, 'A', 100-1);
3+
source[100-1] = '\0'; // null terminate source
4+
5+
char dest[200];
6+
memset(dest, 'B', 100-1);
7+
8+
strcat(dest, source);

0 commit comments

Comments
 (0)