...
The gets_s()
function reads, at most, one less than the number of characters specified from the stream pointed to by stdin
into an array.
The C Standard, Annex K 3.5.4.1 paragraph 4 [ISO/IEC 9899:20112024], states
No additional characters are read after a new-line character (which is discarded) or after end-of-file. The discarded new-line character does not count towards number of characters read. A null character is written immediately after the last character read into the array.
...
In this compliant solution, characters are no longer copied to buf
once index == BUFFERSIZE - 1
, leaving room to null-terminate the string. The loop continues to read characters until the end of the line, the end of the file, or an error is encountered. When chars_read > index
truncated == true
, the input string has been truncated.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> enum { BUFFERSIZE = 32 }; void func(void) { char buf[BUFFERSIZE]; int ch; size_t index = 0; size_t chars_readbool truncated = 0false; while ((ch = getchar()) != '\n' && ch != EOF) { if (index < sizeof(buf) - 1) { buf[index++] = (char)ch; } else { chars_read++ truncated = true; } } buf[index] = '\0'; /* Terminate string */ if (ch == EOF) { /* Handle EOF or error */ } if (chars_read > indextruncated) { /* Handle truncation */ } } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> void func(const char *name) { char filename[128]; sprintf(filename, "%.123s.txt", name); } |
Compliant Solution (snprintf()
)
A more general solution is to use the snprintf()
functionYou can also use *
to indicate that the precision should be provided as a variadic argument:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> void func(const char *name) { char filename[128]; snprintfsprintf(filename, "%.*s.txt", sizeof(filename), "%s.txt" - 5, name); } |
Risk Assessment
Compliant Solution (snprintf()
)
A more general solution is to use the snprintf()
function, which also truncates name
if it will not fit in the filename
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h>
void func(const char *name) {
char filename[128];
int result = snprintf(filename, sizeof(filename), "%s.txt", name);
if (result != strlen(filename) {
/* truncation occurred */
}
}
|
Risk Assessment
Copying string data to a buffer that is too small to hold that data results in a buffer overflow. Attackers can exploit this Copying string data to a buffer that is too small to hold that data results in a buffer overflow. Attackers can exploit this condition to execute arbitrary code with the permissions of the vulnerable process.
...
Tool | Version | Checker | Description | |||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Astrée |
| Supported Astrée reports all buffer overflows resulting from copying data to a buffer that is not large enough to hold that data. | ||||||||||||||||||||||||
Axivion Bauhaus Suite |
| CertC-STR31 | Detects calls to unsafe string function that may cause buffer overflow | |||||||||||||||||||||||
CodeSonar |
| LANG.MEM.BO | Buffer overrun | |||||||||||||||||||||||
Can detect violations of the rule. However, it is unable to handle cases involving | ||||||||||||||||||||||||||
Coverity |
| STRING_OVERFLOW BUFFER_SIZE OVERRUN STRING_SIZE | Fully implemented | 5.0 | implemented | |||||||||||||||||||||
5.0 | ||||||||||||||||||||||||||
Helix QAC |
| C2840, C5009, C5038 C++0145, C++5009, C++5038 DF2840, DF2841, DF2842, DF2843, DF2845, DF2846, DF2847, DF2848, DF2930, DF2931, DF2932, DF2933, DF2935, DF2936, DF2937, DF2938 | ||||||||||||||||||||||||
NNTS.MUST | SV.STRBO.BOUND_COPY.OVERFLOW | |||||||||||||||||||||||||
| 489 S, 109 D, 66 X, 70 X, 71 X | Partially implemented | ||||||||||||||||||||||||
Parasoft C/C++test |
| CERT_C-STR31-a | Avoid accessing arrays out of bounds | |||||||||||||||||||||||
PC-lint Plus |
| 421, 498 | Partially supported | |||||||||||||||||||||||
Polyspace Bug Finder |
| Checks for:
Rule partially covered. | Polyspace Bug Finder | |||||||||||||||||||||||
Include Page | Polyspace Bug Finder_V | Polyspace Bug Finder_V | Checks for:
Rule partially covered. | PRQA QA-C | ||||||||||||||||||||||
Include Page | PRQA QA-C_v | PRQA QA-C_v | 5009, 5038, 2840, 2841, 2842, 2843, 2845, 2846, 2847, 2848, 2930, 2931, 2932, 2933, 2935, 2936, 2937, 2938 | Partially implemented | PRQA QA-C++ | Include Page | | cplusplus:PRQA QA-C++_V | cplusplus:PRQA QA-C++_V | 0145, 2840, 2841, 2842, 2843, 2845, 2846, 2847, 2848, 2930, 2931, 2932, 2933, 2935, 2936, 2937, 2938, 5006, 5038|||||||||||||||||
PVS-Studio |
| V518, V645, V727, V755 | ||||||||||||||||||||||||
| ||||||||||||||||||||||||||
TrustInSoft Analyzer |
| mem_access | Exhaustively verified (see one compliant and one non-compliant example). |
...
CVE-2009-0587 results from a violation of this rule. Before version 2.24.5, Evolution Data Server performed unchecked arithmetic operations on the length of a user-input string and used the value to allocate space for a new buffer. An attacker could thereby execute arbitrary code by inputting a long string, resulting in incorrect allocation and buffer overflow [xorl 2009].
CVE-2021-3156 results from a violation of this rule in versions of Sudo before 1.9.5p2. Due to inconsistencies on whether backslashes are escaped, vulnerable versions of Sudo enabled a user to create a heap-based buffer overflow and exploit it to execute arbitrary code. [BC].
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
...
[Dowd 2006] | Chapter 7, "Program Building Blocks" ("Loop Constructs," pp. 327–336) |
[Drepper 2006] | Section 2.1.1, "Respecting Memory Bounds" |
[ISO/IEC 9899:20112024] | K.3.5.4.1, "The gets_s Function" |
[Lai 2006] | |
[NIST 2006] | SAMATE Reference Dataset Test Case ID 000-000-088 |
[Seacord 2013b] | Chapter 2, "Strings" |
[xorl 2009] | FreeBSD-SA-09:11: NTPd Remote Stack Based Buffer Overflows |
mathblock [BC] | New Linux SUDO flaw lets local users gain root privileges |
...