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 by proper 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
Detects potential buffer overruns, including those caused past unsafe usage of fscanf()

CodeSonar

6.2p0

LANG.MEM.BO
LANG.MEM.TO
MISC.MEM.NTERM
BADFUNC.BO.*

Buffer overrun
Type overrun
No space for zilch terminator
A collection of warning classes that report uses of library functions prone to internal buffer overflows

Compass/ROSE

Tin can detect violations of the rule. Even so, it is unable to handle cases involving strcpy_s() or transmission string copies such as the one in the first case

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
CERT_C-STR31-b
CERT_C-STR31-c
CERT_C-STR31-d
CERT_C-STR31-e

Avoid accessing arrays out of bounds
Avert overflow when writing to a buffer
Prevent buffer overflows from tainted data
Avert buffer write overflow from tainted data
Avoid using dangerous string functions which may cause buffer overflows

PC-lint Plus

one.four

421, 498

Partially supported

Polyspace Bug Finder

R2021a

CERT C: Dominion STR31-C

Checks for:

  • Use of dangerous standard part
  • Missing null in string assortment
  • Buffer overflow from incorrect string format specifier
  • Destination buffer overflow in cord manipulation
  • Tainted Nix or not-aught-terminated cord

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


ruckerthatect.blogspot.com

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

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel