C Read One Character at a Time
Copying data to a buffer that is not large plenty to concord that data results in a buffer overflow. Buffer overflows occur frequently when manipulating strings [Seacord 2013b]. To prevent such errors, either limit copies through truncation or, preferably, ensure that the destination is of sufficient size to agree the character data to be copied and the null-termination grapheme. (See STR03-C. Do not inadvertently truncate a string.)
When strings alive on the heap, this dominion is a specific instance of MEM35-C. Allocate sufficient memory for an object. Because strings are represented equally arrays of characters, this rule is related to both ARR30-C. Do not form or employ out-of-premises pointers or array subscripts and ARR38-C. Guarantee that library functions do not form invalid pointers.
Noncompliant Code Example (Off-by-One Error)
This noncompliant code example demonstrates an off-by-ane error [Dowd 2006]. The loop copies data from src
to dest
. However, because the loop does not account for the null-termination graphic symbol, it may be incorrectly written i byte past the end of dest
.
#include <stddef.h> void copy(size_t n, char src[n], char dest[n]) { size_t i; for (i = 0; src[i] && (i < n); ++i) { dest[i] = src[i]; } dest[i] = '\0'; }
Compliant Solution (Off-by-One Error)
In this compliant solution, the loop termination status is modified to account for the null-termination grapheme that is appended to dest
:
#include <stddef.h> void copy(size_t north, char src[northward], char dest[due north]) { size_t i; for (i = 0; src[i] && (i < n - 1); ++i) { dest[i] = src[i]; } dest[i] = '\0'; }
Noncompliant Code Instance (gets()
)
Thegets()
office, which was deprecated in the C99 Technical Corrigendum 3 and removed from C11, is inherently unsafe and should never be used because it provides no way to command how much data is read into a buffer fromstdin
. This noncompliant code instance assumes thatgets()
will non read more thanBUFFER_SIZE - 1
characters fromstdin
. This is an invalid assumption, and the resulting performance tin outcome in a buffer overflow.
Thegets()
function reads characters from thestdin
into a destination array until end-of-file is encountered or a newline grapheme is read. Any newline character is discarded, and a null character is written immediately after the final character read into the array.
#include <stdio.h> #define BUFFER_SIZE 1024 void func(void) { char buf[BUFFER_SIZE]; if (gets(buf) == Nix) { /* Handle fault */ } }
See also MSC24-C. Do not utilise deprecated or obsolescent functions.
Compliant Solution (fgets()
)
Thefgets()
function reads, at most, one less than the specified number of characters from a stream into an assortment. This solution is compliant because the number of characters copied fromstdin
tobuf
cannot exceed the allocated memory:
#include <stdio.h> #include <cord.h> enum { BUFFERSIZE = 32 }; void func(void) { char buf[BUFFERSIZE]; int ch; if (fgets(buf, sizeof(buf), stdin)) { /* fgets() succeeded; scan for newline grapheme */ char *p = strchr(buf, '\n'); if (p) { *p = '\0'; } else { /* Newline not constitute; flush stdin to end of line */ while ((ch = getchar()) != '\n' && ch != EOF) ; if (ch == EOF && !feof(stdin) && !ferror(stdin)) { /* Character resembles EOF; handle fault */ } } } else { /* fgets() failed; handle fault */ } }
Thefgets()
function is not a strict replacement for thegets()
part becausefgets()
retains the newline character (if read) and may also return a partial line. It is possible to usefgets()
to safely procedure input lines also long to store in the destination array, merely this is non recommended for performance reasons. Consider using one of the post-obit compliant solutions when replacinggets()
.
Compliant Solution (gets_s()
)
Thegets_s()
function reads, at most, one less than the number of characters specified from the stream pointed to bystdin
into an array.
The C Standard, Annex Thou [ISO/IEC 9899:2011], states
No additional characters are read afterwards a new-line character (which is discarded) or afterwards end-of-file. The discarded new-line character does not count towards number of characters read. A nada character is written immediately after the last character read into the assortment.
If end-of-file is encountered and no characters have been read into the destination assortment, or if a read mistake occurs during the operation, then the start character in the destination assortment is set to the zero character and the other elements of the array take unspecified values:
#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> enum { BUFFERSIZE = 32 }; void func(void) { char buf[BUFFERSIZE]; if (gets_s(buf, sizeof(buf)) == NULL) { /* Handle error */ } }
Compliant Solution (getline()
, POSIX)
Thegetline()
function is like to the fgets()
function just can dynamically allocate memory for the input buffer. If passed a zilch pointer,getline()
dynamically allocates a buffer of sufficient size to hold the input. If passed a pointer to dynamically allocated storage that is too small to agree the contents of the string, thegetline()
part resizes the buffer, usingrealloc()
, rather than truncating the input. If successful, thegetline()
function returns the number of characters read, which tin can be used to determine if the input has whatsoever zippo characters earlier the newline. The getline()
function works only with dynamically allocated buffers. Allocated memory must be explicitly deallocated by the caller to avoid retention leaks. (See MEM31-C. Free dynamically allocated retentivity when no longer needed.)
#include <stdio.h> #include <stdlib.h> #include <string.h> void func(void) { int ch; size_t buffer_size = 32; char *buffer = malloc(buffer_size); if (!buffer) { /* Handle error */ render; } if ((ssize_t size = getline(&buffer, &buffer_size, stdin)) == -1) { /* Handle error */ } else { char *p = strchr(buffer, '\n'); if (p) { *p = '\0'; } else { /* Newline not found; flush stdin to end of line */ while ((ch = getchar()) != '\n' && ch != EOF) ; if (ch == EOF && !feof(stdin) && !ferror(stdin)) { /* Grapheme resembles EOF; handle error */ } } } free (buffer); }
Note that the getline()
function uses an in-band error indicator, in violation of ERR02-C. Avoid in-ring error indicators.
Noncompliant Lawmaking Instance (getchar()
)
Reading one character at a time provides more flexibility in decision-making behavior, though with additional performance overhead. This noncompliant code example uses thegetchar()
function to read one grapheme at a time fromstdin
instead of reading the entire line at in one case. Thestdin
stream is read until end-of-file is encountered or a newline character is read. Whatsoever newline character is discarded, and a cipher character is written immediately after the last character read into the assortment. Similar to the noncompliant lawmaking example that invokes gets()
, there are no guarantees that this code will not issue in a buffer overflow.
#include <stdio.h> enum { BUFFERSIZE = 32 }; void func(void) { char buf[BUFFERSIZE]; char *p; int ch; p = buf; while ((ch = getchar()) != '\n' && ch != EOF) { *p++ = (char)ch; } *p++ = 0; if (ch == EOF) { /* Handle EOF or error */ } }
After the loop ends, if ch == EOF
, the loop has read through to the finish of the stream without encountering a newline character, or a read error occurred before the loop encountered a newline character. To conform to FIO34-C. Distinguish between characters read from a file and EOF or WEOF, the error-handling code must verify that an end-of-file or fault has occurred by calling feof()
or ferror()
.
Compliant Solution (getchar()
)
In this compliant solution, characters are no longer copied tobuf
one timeindex == BUFFERSIZE - 1
, leaving room to null-stop the string. The loop continues to read characters until the finish of the line, the end of the file, or an error is encountered. When chars_read > index
, the input cord has been truncated.
#include <stdio.h> enum { BUFFERSIZE = 32 }; void func(void) { char buf[BUFFERSIZE]; int ch; size_t index = 0; size_t chars_read = 0; while ((ch = getchar()) != '\n' && ch != EOF) { if (index < sizeof(buf) - 1) { buf[alphabetize++] = (char)ch; } chars_read++; } buf[index] = '\0'; /* Terminate cord */ if (ch == EOF) { /* Handle EOF or error */ } if (chars_read > index) { /* Handle truncation */ } }
Noncompliant Code Example (fscanf()
)
In this noncompliant instance, the call to fscanf()
can effect in a write exterior the grapheme array buf
:
#include <stdio.h> enum { BUF_LENGTH = 1024 }; void get_data(void) { char buf[BUF_LENGTH]; if (one != fscanf(stdin, "%south", buf)) { /* Handle error */ } /* Balance of function */ }
Compliant Solution (fscanf()
)
In this compliant solution, the telephone call to fscanf()
is constrained not to overflow buf
:
#include <stdio.h> enum { BUF_LENGTH = 1024 }; void get_data(void) { char buf[BUF_LENGTH]; if (ane != fscanf(stdin, "%1023s", buf)) { /* Handle fault */ } /* Rest of function */ }
Noncompliant Lawmaking Example (argv
)
In a hosted environs, arguments read from the control line are stored in process retention. The office primary()
, called at program startup, is typically declared as follows when the program accepts command-line arguments:
int main(int argc, char *argv[]) { /* ... */ }
Command-line arguments are passed to main()
every bit pointers to strings in the assortment members argv[0]
through argv[argc - 1]
. If the value of argc
is greater than 0, the string pointed to by argv[0]
is, by convention, the program name. If the value of argc
is greater than ane, the strings referenced by argv[1]
through argv[argc - 1]
are the plan arguments.
Vulnerabilities tin can occur when inadequate infinite is allocated to copy a control-line argument or other program input. In this noncompliant code case, an aggressor tin dispense the contents of argv[0]
to cause a buffer overflow:
#include <string.h> int primary(int argc, char *argv[]) { /* Ensure argv[0] is non null */ const char *const name = (argc && argv[0]) ? argv[0] : ""; char prog_name[128]; strcpy(prog_name, name); return 0; }
Compliant Solution (argv
)
The strlen()
function can be used to decide the length of the strings referenced by argv[0]
through argv[argc - ane]
so that acceptable memory can exist dynamically allocated.
#include <stdlib.h> #include <string.h> int primary(int argc, char *argv[]) { /* Ensure argv[0] is not zilch */ const char *const name = (argc && argv[0]) ? argv[0] : ""; char *prog_name = (char *)malloc(strlen(name) + 1); if (prog_name != Nothing) { strcpy(prog_name, name); } else { /* Handle error */ } free(prog_name); render 0; }
Recall to add a byte to the destination string size to adapt the nil-termination grapheme.
Compliant Solution (argv
)
The strcpy_s()
function provides additional safeguards, including accepting the size of the destination buffer as an additional argument. (See STR07-C. Use the bounds-checking interfaces for cord manipulation.)
#ascertain __STDC_WANT_LIB_EXT1__ ane #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { /* Ensure argv[0] is non nix */ const char *const name = (argc && argv[0]) ? argv[0] : ""; char *prog_name; size_t prog_size; prog_size = strlen(name) + 1; prog_name = (char *)malloc(prog_size); if (prog_name != NULL) { if (strcpy_s(prog_name, prog_size, proper name)) { /* Handle error */ } } else { /* Handle error */ } /* ... */ free(prog_name); return 0; }
The strcpy_s()
role can be used to copy data to or from dynamically allocated memory or a statically allocated array. If insufficient infinite is bachelor, strcpy_s()
returns an fault.
Compliant Solution (argv
)
If an argument will non be modified or concatenated, there is no reason to make a re-create of the cord. Not copying a string is the best way to forestall a buffer overflow and is also the most efficient solution. Intendance must exist taken to avoid assuming that argv[0]
is non-null.
int main(int argc, char *argv[]) { /* Ensure argv[0] is not nil */ const char * const prog_name = (argc && argv[0]) ? argv[0] : ""; /* ... */ return 0; }
Noncompliant Lawmaking Example (getenv()
)
According to the C Standard, 7.22.4.6 [ISO/IEC 9899:2011]
The
getenv
office searches an environs list, provided past the host environment, for a string that matches the cord pointed to byproper noun
. The set of surround names and the method for altering the environment list are implementation defined.
Environment variables can exist arbitrarily big, and copying them into fixed-length arrays without first determining the size and allocating adequate storage tin result in a buffer overflow.
#include <stdlib.h> #include <string.h> void func(void) { char vitrify[256]; char *editor = getenv("EDITOR"); if (editor == Cipher) { /* EDITOR environment variable not set up */ } else { strcpy(buff, editor); } }
Compliant Solution (getenv()
)
Environmental variables are loaded into process memory when the plan is loaded. Every bit a event, the length of these strings can be adamant by calling the strlen()
function, and the resulting length can be used to classify adequate dynamic memory:
#include <stdlib.h> #include <string.h> void func(void) { char *vitrify; char *editor = getenv("EDITOR"); if (editor == NULL) { /* EDITOR surround variable non prepare */ } else { size_t len = strlen(editor) + one; buff = (char *)malloc(len); if (vitrify == NULL) { /* Handle error */ } memcpy(buff, editor, len); complimentary(buff); } }
Noncompliant Code Example (sprintf()
)
In this noncompliant code instance, name
refers to an external string; it could have originated from user input, the file organization, or the network. The program constructs a file name from the cord in preparation for opening the file.
#include <stdio.h> void func(const char *name) { char filename[128]; sprintf(filename, "%s.txt", proper noun); }
Considering the sprintf()
function makes no guarantees regarding the length of the generated string, a sufficiently long string in proper noun
could generate a buffer overflow.
Compliant Solution (sprintf()
)
The buffer overflow in the preceding noncompliant example tin can exist prevented by adding a precision to the %s
conversion specification. If the precision is specified, no more than that many bytes are written. The precision 123
in this compliant solution ensures that filename
can contain the commencement 123 characters of name
, the .txt
extension, and the cipher terminator.
#include <stdio.h> void func(const char *proper name) { char filename[128]; sprintf(filename, "%.123s.txt", proper noun); }
You can also use*
to indicate that the precision should exist provided as a variadic argument:
#include <stdio.h> void func(const char *proper name) { char filename[128]; sprintf(filename, "%.*due south.txt", sizeof(filename) - 5, name); }
Compliant Solution (snprintf()
)
A more full general solution is to use the snprintf()
function:
#include <stdio.h> void func(const char *name) { char filename[128]; snprintf(filename, sizeof(filename), "%s.txt", name); }
Risk Assessment
Copying string data to a buffer that is too pocket-size to hold that data results in a buffer overflow. Attackers can exploit this status to execute arbitrary code with the permissions of the vulnerable process.
Dominion | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
STR31-C | Loftier | Likely | Medium | P18 | L1 |
Automated Detection
Assortment access out of bounds, Buffer overflow from wrong string format specifier, Destination buffer overflow in cord manipulation, Invalid apply of standard library string routine, Missing null in string assortment, Pointer access out of bounds, Tainted NULL or non-null-terminated string, Use of unsafe standard role
Tool | Version | Checker | Clarification |
---|---|---|---|
Astrée | twenty.x | | Supported Astrée reports all buffer overflows resulting from copying information to a buffer that is not large enough to hold that data. |
Axivion Bauhaus Suite | vii.2.0 | CertC-STR31 | Detects calls to dangerous string function that may cause buffer overflow |
CodeSonar | 6.2p0 | LANG.MEM.BO | Buffer overrun |
Compass/ROSE | Tin can detect violations of the rule. Even so, it is unable to handle cases involving | ||
Coverity | 2017.07 | STRING_OVERFLOW BUFFER_SIZE OVERRUN STRING_SIZE | Fully implemented |
Fortify SCA | v.0 | ||
Helix QAC | 2021.3 | C2840, C2841, C2842, C2843, C2845, C2846, C2847, C2848, C2930, C2931, C2932, C2933, C2935, C2936, C2937, C2938 C++0145, C++2840, C++2841, C++2842, C++2843, C++2845, C++2846, C++2847, C++2848, C++2930, C++2931, C++2932, C++2933, C++2935, C++2936, C++2937, C++2938 | |
Klocwork | 2021.4 | SV.FMT_STR.BAD_SCAN_FORMAT SV.UNBOUND_STRING_INPUT.FUNC | |
LDRA tool suite | 9.7.1 | 489 Due south, 109 D, 66 10, lxx Ten, 71 10 | Partially implemented |
Parasoft C/C++test | 2021.2 | CERT_C-STR31-a | Avoid accessing arrays out of bounds |
PC-lint Plus | one.four | 421, 498 | Partially supported |
Polyspace Bug Finder | R2021a | CERT C: Dominion STR31-C | Checks for:
Dominion partially covered. |
PRQA QA-C | 9.7 | 5009, 5038, 2840, 2841, 2842, 2843, 2845, 2846, 2847, 2848, 2930, 2931, 2932, 2933, 2935, 2936, 2937, 2938 | Partially implemented |
PRQA QA-C++ | 4.four | 0145, 2840, 2841, 2842, 2843, 2845, 2846, 2847, 2848, 2930, 2931, 2932, 2933, 2935, 2936, 2937, 2938, 5006, 5038 | |
PVS-Studio | vii.17 | V518, V645, V727, V755 | |
Splint | 3.i.1 | ||
TrustInSoft Analyzer | 1.38 | mem_access | Exhaustively verified (see one compliant and one non-compliant example). |
Related Vulnerabilities
CVE-2009-1252 results from a violation of this rule. The Network Time Protocol daemon (NTPd), before versions 4.2.4p7 and 4.2.5p74, contained calls to sprintf
that allow an attacker to execute capricious code by overflowing a character array [xorl 2009].
CVE-2009-0587 results from a violation of this dominion. Earlier 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 infinite for a new buffer. An assailant could thereby execute arbitrary lawmaking by inputting a long cord, resulting in incorrect allocation and buffer overflow [xorl 2009].
CVE-2021-3156 results from a violation of this rule in versions of Sudo before one.ix.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 lawmaking. [BC].
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Cardinal here (explains table format and definitions)
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-122 and STR31-C
STR31-C = Matrimony( CWE-122, list) where listing =
- Buffer overflows on strings in the stack or data segment
CWE-125 and STR31-C
Independent( ARR30-C, ARR38-C, EXP39-C, INT30-C)
STR31-C = Subset( Marriage( ARR30-C, ARR38-C))
STR32-C = Subset( ARR38-C)
Intersection( STR31-C, CWE-125) =
- Directly reading beyond the end of a string
STR31-C – CWE-125 =
- Directly writing across the stop of a cord
CWE-125 – STR31-C =
- Reading across a non-string array
- Reading beyond a string using library functions
CWE-676 and STR31-C
- Independent( ENV33-C, CON33-C, STR31-C, EXP33-C, MSC30-C, ERR34-C)
- STR31-C implies that several C string re-create functions, like strcpy() are dangerous.
Intersection( CWE-676, STR31-C) =
- Buffer Overflow resulting from invocation of the post-obit dangerous functions:
- gets(), fscanf(), strcpy(), sprintf()
STR31-C – CWE-676 =
- Buffer overflow that does not involve the dangerous functions listed above.
CWE-676 - STR31-C =
- Invocation of other dangerous functions
CWE-121 and STR31-C
STR31-C = Union( CWE-121, list) where list =
- Buffer overflows on strings in the heap or data segment
CWE-123 and STR31-C
Independent(ARR30-C, ARR38-C)
STR31-C = Subset( Union( ARR30-C, ARR38-C))
STR32-C = Subset( ARR38-C)
Intersection( CWE-123, STR31-C) =
- Buffer overflow that overwrites a (unrelated) pointer with untrusted information
STR31-C – CWE-123 =
- Buffer overflow that does non overwrite a (unrelated) pointer
CWE-123 – STR31-C =
- Arbitrary writes that practise not involve buffer overflows
CWE-119 and STR31-C
Independent( ARR30-C, ARR38-C, ARR32-C, INT30-C, INT31-C, EXP39-C, EXP33-C, FIO37-C)
STR31-C = Subset( Union( ARR30-C, ARR38-C))
STR32-C = Subset( ARR38-C)
CWE-119 = Spousal relationship( STR31-C, listing) where list =
- Out-of-premises reads or writes that are non created by string copy operations
CWE-193 and STR31-C
Intersection( CWE-193, STR31-C) = Ø
CWE-193 involves an integer ciphering error (typically off-by-one), which is oftentimes a precursor to (slight) buffer overflow. Still the two errors occur in different operations and are thus unrelated.
Bibliography
Source: 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
0 Response to "C Read One Character at a Time"
Post a Comment