Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updated references from C11->C23

All The majority of the standard library functions, including inputI/output O functions and memory allocation functions, return either a valid value or a value of the correct return type that indicates an error (for example, −1 or a null pointer). Assuming that all calls to such functions will succeed and failing to check the return value for an indication of an error is a dangerous practice that may lead to unexpected or undefined behavior when an error occurs. It is essential that programs detect and appropriately handle all errors in accordance with an error-handling policy, as discussed in ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy. Anchornce_setlocalence_setlocale

The successful completion or failure of each of the standard library functions listed in the following table below shall be determined either by comparing the function’s return value with the value listed in the column labeled “Error Return” or by calling one of the library functions mentioned in the footnotes to the same column..

Standard Library Functions

Function

Successful Return

Error Return

aligned_alloc()

pointer

Pointer to space

NULL

asctime_s()

zero

0

nonzero

Nonzero

at_quick_exit()

zero

0

nonzero

Nonzero

atexit()

zero

0

nonzero

Nonzero

bsearch()

pointer

Pointer to matching element

NULL

bsearch_s()

pointer

Pointer to matching element

NULL

btowc()

converted

Converted wide character

WEOF

c16rtomb()

number

Number of bytes

(size_t)(-1)

c32rtomb()

number

Number of bytes

(size_t)(-1)

calloc()

pointer

Pointer to space

NULL

clock()

processor

Processor time

(clock_t)(-1)

cnd_broadcast()

thrd_success

thrd_error

cnd_init()

thrd_success

thrd_nomem or thrd_error

cnd_signal()

thrd_success

thrd_error

cnd_timedwait()

thrd_success

thrd_timedout or thrd_error

cnd_wait()

thrd_success

thrd_error

ctime_s()

zero

0

nonzero

Nonzero

fclose()

zero

0

EOF (negative)

fflush()

zero

0

EOF (negative)

fgetc()

character

Character read

EOF

2

1

fgetpos()

zero

0

Nonzero, errno > 0

nonzero

fgets()

pointer

Pointer to string

NULL

fgetwc()

wide

Wide character read

WEOF

2

1

fopen()

pointer

Pointer to stream

NULL

fopen_s()

zero

0

nonzero

Nonzero

fprintf()

number

Number of characters (nonnegative)

negative

Negative

fprintf_s()

number

Number of characters (nonnegative)

negative

Negative

fputc()

character

Character written

EOF

1

2

fputs()

nonnegative

Nonnegative

EOF (negative)

fputwc()Wide character written WEOF

fputws()

nonnegative

Nonnegative

EOF (negative)

fread()

elements

Elements read

elements

Elements read

freopen()

pointer

Pointer to stream

NULL

freopen_s()

zero

0

nonzero

Nonzero

fscanf()

number

Number of conversions (nonnegative)

EOF (negative)

fscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

fseek()

zero

0

nonzero

Nonzero

fsetpos()

zero

0

Nonzero, errno > 0

nonzero

ftell()

file

File position

−1L, errno > 0

fwprintf()

number

Number of wide characters (nonnegative)

negative

Negative

fwprintf_s()

number

Number of wide characters (nonnegative)

negative

Negative

fwrite()

elements

Elements written

elements

Elements written

fwscanf()

number

Number of conversions (nonnegative)

EOF (negative)

fwscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

getc()

character

Character read

EOF

2

1

getchar()

character

Character read

EOF

2

1

getenv()

pointer

Pointer to string

NULL

getenv_s()

pointer

Pointer to string

NULL

gets_s()

pointer

Pointer to string

NULL

getwc()

wide

Wide character read

WEOF

getwchar()

wide

Wide character read

WEOF

gmtime()

pointer

Pointer to broken-down time

NULL

gmtime_s()

pointer

Pointer to broken-down time

NULL

localtime()

pointer

Pointer to broken-down time

NULL

localtime_s()

pointer

Pointer to broken-down time

NULL

malloc()

pointer

Pointer to space

NULL

mblen(), s != NULL

number

Number of bytes

−1

mbrlen(), s != NULL

number

Number of bytes or status

(size_t)(-1)

mbrtoc16()

number

Number of bytes or status

(size_t)(-1), errno == EILSEQ

mbrtoc32()

number

Number of bytes or status

(size_t)(-1), errno == EILSEQ

mbrtowc(), s != NULL

number

Number of bytes or status

(size_t)(-1), errno == EILSEQ

mbsrtowcs()

number

Number of non-null elements

(size_t)(-1), errno == EILSEQ

mbsrtowcs_s()

zero

0

nonzero

Nonzero

mbstowcs()

number

Number of non-null elements

(size_t)(-1)

mbstowcs_s()

zero

0

nonzero

Nonzero

mbtowc(), s != NULL

number

Number of bytes

−1

memchr()

pointer

Pointer to located character

NULL

mktime()

calendar

Calendar time

(time_t)(-1)

mtx_init()

thrd_success

thrd_error

mtx_lock()

thrd_success

thrd_error

mtx_timedlock()

thrd_success

thrd_timedout or thrd_error

mtx_trylock()

thrd_success

thrd_busy or thrd_error

mtx_unlock()

thrd_success

thrd_error

printf_s()

number

Number of characters (nonnegative)

negative

Negative

putc()

character

Character written

EOF

1

2

putwc()

wide

Wide character written

WEOF

raise()

zero

0

nonzero

Nonzero

realloc()

pointer

Pointer to space

NULL

remove()

zero

0

nonzero

Nonzero

rename()

zero

0

nonzero

Nonzero

setlocale()

pointer

Pointer to string

NULL

setvbuf()

zero

0

nonzero

Nonzero

scanf()

number

Number of conversions (nonnegative)

EOF (negative)

scanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

signal()

pointer

Pointer to previous function

SIG_ERR, errno > 0

snprintf()

number

Number of characters that would be written (nonnegative)

negative

Negative

snprintf_s()

number

Number of characters that would be written (nonnegative)

negative

Negative

sprintf()

number

Number of non-null characters written

negative

Negative

sprintf_s()

number

Number of non-null characters written

negative

Negative

sscanf()

number

Number of conversions (nonnegative)

EOF (negative)

sscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

strchr()

pointer

Pointer to located character

NULL

strerror_s()

zero

0

nonzero

Nonzero

strftime()

number

Number of non-null characters

zero

0

strpbrk()

pointer

Pointer to located character

NULL

strrchr()

pointer

Pointer to located character

NULL

strstr()

pointer

Pointer to located string

NULL

strtod()

converted

Converted value

zero

0, errno == ERANGE

strtof()

converted

Converted value

zero

0, errno == ERANGE

strtoimax()

converted

Converted value

INTMAX_MAX or INTMAX_MIN, errno == ERANGE

strtok()

pointer

Pointer to first character of a token

NULL

strtok_s()

pointer

Pointer to first character of a token

NULL

strtol()

converted

Converted value

LONG_MAX or LONG_MIN, errno == ERANGE

strtold()

converted

Converted value

zero

0, errno == ERANGE

strtoll()

converted

Converted value

LLONG_MAX or LLONG_MIN, errno == ERANGE

strtoumax()

converted

Converted value

UINTMAX_MAX, errno == ERANGE

strtoul()

converted

Converted value

ULONG_MAX, errno == ERANGE

strtoull()

converted

Converted value

ULLONG_MAX, errno == ERANGE

strxfrm()

length

Length of transformed string

>= n

swprintf()

number

Number of non-null wide characters

negative

Negative

swprintf_s()

number

Number of non-null wide characters

negative

Negative

swscanf()

number

Number of conversions (nonnegative)

EOF (negative)

swscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

thrd_create()

thrd_success

thrd_nomem or thrd_error

thrd_detach()

thrd_success

thrd_error

thrd_join()

thrd_success

thrd_error

thrd_sleep()

zero

0

negative

Negative

time()

calendar

Calendar time

(time_t)(-1)

timespec_get()

base

Base

zero

0

tmpfile()

pointer

Pointer to stream

NULL

tmpfile_s()

zero

0

nonzero

Nonzero

tmpnam()

non

Non-null pointer

NULL

tmpnam_s()

zero

0

nonzero

Nonzero

tss_create()

thrd_success

thrd_error

tss_get()

value

Value of thread-specific storage

zero

0

tss_set()

thrd_success

thrd_error

ungetc()

character

Character pushed back

EOF (

negative;

see below)

ungetwc()

character

Character pushed back

WEOF

(negative)

vfprintf()

number

Number of characters (nonnegative)

negative

Negative

vfprintf_s()

number

Number of characters (nonnegative)

negative

Negative

vfscanf()

number

Number of conversions (nonnegative)

EOF (negative)

vfscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

vfwprintf()

number

Number of wide characters (nonnegative)

negative

Negative

vfwprintf_s()

number

Number of wide characters (nonnegative)

negative

Negative

vfwscanf()

number

Number of conversions (nonnegative)

EOF (negative)

vfwscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

vprintf_s()

number

Number of characters (nonnegative)

negative

Negative

vscanf()

number

Number of conversions (nonnegative)

EOF (negative)

vscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

vsnprintf()

number

Number of characters that would be written (nonnegative)

negative

Negative

vsnprintf_s()

number

Number of characters that would be written (nonnegative)

negative

Negative

vsprintf()

number

Number of non-null characters (nonnegative)

negative

Negative

vsprintf_s()

number

Number of non-null characters (nonnegative)

negative

Negative

vsscanf()

number

Number of conversions (nonnegative)

EOF (negative)

vsscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

vswprintf()

number

Number of non-null wide characters

negative

Negative

vswprintf_s()

number

Number of non-null wide characters

negative

Negative

vswscanf()

number

Number of conversions (nonnegative)

EOF (negative)

vswscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

vwprintf_s()

number

Number of wide characters (nonnegative)

negative

Negative

vwscanf()

number

Number of conversions (nonnegative)

EOF (negative)

vwscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

wcrtomb()

number

Number of bytes stored

(size_t)(-1)

wcschr()

pointer

Pointer to located wide character

NULL

wcsftime()

number

Number of non-null wide characters

zero

0

wcspbrk()

pointer

Pointer to located wide character

NULL

wcsrchr()

pointer

Pointer to located wide character

NULL

wcsrtombs()

number

Number of non-null bytes

(size_t)(-1), errno == EILSEQ

wcsrtombs_s()

zero

0

nonzero

Nonzero

wcsstr()

pointer

Pointer to located wide string

NULL

wcstod()

converted

Converted value

zero

0, errno == ERANGE

wcstof()

converted

Converted value

zero

0, errno == ERANGE

wcstoimax()

converted

Converted value

INTMAX_MAX or INTMAX_MIN, errno == ERANGE

wcstok()

pointer

Pointer to first wide character of a token

NULL

wcstok_s()

pointer

Pointer to first wide character of a token

NULL

wcstol()

converted

Converted value

LONG_MAX or LONG_MIN, errno == ERANGE

wcstold()

converted

Converted value

zero

0, errno == ERANGE

wcstoll()

converted

Converted value

LLONG_MAX or LLONG_MIN, errno == ERANGE

wcstombs()

number

Number of non-null bytes

(size_t)(-1)

wcstombs_s()

zero

0

nonzero

Nonzero

wcstoumax()

converted

Converted value

UINTMAX_MAX, errno == ERANGE

wcstoul()

converted

Converted value

ULONG_MAX, errno == ERANGE

wcstoull()

converted

Converted value

ULLONG_MAX, errno == ERANGE

wcsxfrm()

length

Length of transformed wide string

>= n

wctob()

converted

Converted character

EOF

wctomb(), s != NULL

number

Number of bytes stored

−1

wctomb_s(), s != NULL

number

Number of bytes stored

−1

wctrans()

valid

Valid argument to towctrans

zero

0

wctype()

valid

Valid argument to iswctype

zero

0

wmemchr()

pointer

Pointer to located wide character

NULL

wprintf_s()

number

Number of wide characters (nonnegative)

negative

Negative

wscanf()

number

Number of conversions (nonnegative)

EOF (negative)

wscanf_s()

number

Number of conversions (nonnegative)

EOF (negative)

When Note: According to FIO35-C. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char) applies, callers shall determine the success or failure of the functions above should verify end-of-file and file errors for the functions in this table as follows:

...

1 By calling ferror() and feof()
2 By calling ferror()

AnchorungetcungetcThe ungetc() function does not set the error indicator even when it fails, so it is not possible to check for errors reliably unless it is known that the argument is not equal to EOF. C99 states that "one character of pushback is guaranteed," .

The C Standard 7.31.3.10 paragraph 3 [ISO/IEC 9899:2024] states that

 )ne wide character of pushback is guaranteed...

so this should not be an issue if, at most, one character is ever pushed back before reading again. (See FIO13-C. Never push back anything other than one read character.)

Noncompliant Code Example (setlocale())

In addition to the C standard library functions above, POSIX defines the following functions designed specifically to dynamically allocate memory.

Function

Successful Return

Error Return

errno

fmemopen()

pointer to a FILE object

NULL

ENOMEM

open_memstream()

pointer to a FILE object

NULL

ENOMEM

posix_memalign()

0

non-zero #2

unchanged #2

...

this noncompliant code example, the function utf8_to_wcs() attempts to convert a sequence of UTF-8 characters to wide characters. It first invokes setlocale() to set the global locale to the implementation-defined en_US.UTF-8 but does not check for failure. The setlocale() function will fail by returning a null pointer, for example, when the locale is not installed. The function may fail for other reasons as well, such as the lack of resources.  Depending on the sequence of characters pointed to by utf8, the subsequent call to mbstowcs() may fail or result in the function storing an unexpected sequence of wide characters in the supplied buffer wcs.

Code Block
bgColor#FFcccc
langc
#include <locale.h>
#include <stdlib.h>
 
int utf8_to_wcs(wchar_t *wcs, size_t n, const char *utf8,
                size_t *size) {
  if (NULL == size) {
    return -1;
  }
  setlocale(LC_CTYPE, 

...

Noncompliant Code Example (setlocale())

...

"en_US.UTF-8");
  *size = mbstowcs(wcs, utf8, n);
  return 0;
}

Compliant Solution (setlocale())

This compliant solution checks the value returned by setlocale() and avoids calling mbstowcs() if the function fails. The function also takes care to restore the locale to its initial setting before returning control to the caller but does not check for failure. setlocale() will fail by returning a null pointer, for example, when the locale is not installed. (The function may fail for other reasons, as well, such as the lack of resources.) Depending on the sequence of characters pointed to by utf8, the subsequent call to mbstowcs() may fail or result in the function storing an unexpected sequence of wide characters in the supplied buffer ucs.

Code Block
bgColor#FFcccc#ccccFF
langc
size_t#include <locale.h>
#include <stdlib.h>
 
int utf8_to_ucswcs(wchar_t *ucswcs, size_t n, const char *utf8),
  {
  setlocale(LC_CTYPE, "en_            size_t *size) {
  if (NULL == size) {
    return -1;
  }
  const char *save = setlocale(LC_CTYPE, "en_US.UTF-8");
  returnif mbstowcs(ucs, utf8, n);
}

...

NULL == save) {
    return -1;
  }

  *size = mbstowcs(wcs, utf8, n);
  if (NULL == setlocale(LC_CTYPE, save)) {
    return -1;
  }
  return 0;
}

Noncompliant Code Example (calloc())

In this noncompliant code example, temp_num, tmp2, and num_of_records are derived from a tainted source. Consequently, an attacker can easily cause calloc() to fail by providing a large value for num_of_records

Code Block
bgColor#FFCCCC
langc
#include <stdlib.h>
#include <string.h>
 
enum { SIG_DESC_SIZE = 32 };

typedef struct {
  char sig_desc[SIG_DESC_SIZE];
} signal_info;
 
void func(size_t num_of_records, size_t temp_num,
          const char *tmp2, size_t tmp2_size_bytes) {
  signal_info *start = (signal_info *)calloc(num_of_records,
                                          sizeof(signal_info));

  if (tmp2 == NULL) {
    /* Handle error */
  } else if (temp_num > num_of_records || temp_num == 0) {
    /* Handle error */
  } else if (tmp2_size_bytes < SIG_DESC_SIZE) {
    /* Handle error */
  }

  signal_info *point = start + temp_num - 1;
  memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE);
  point->sig_desc[SIG_DESC_SIZE - 1] = '\0';
  /* ... */
  free(start);
}

When calloc() fails, it returns a null pointer that is assigned to start. If start is null, an attacker can provide a value for temp_num that, when scaled by sizeof(signal_info), references a writable address to which control is eventually transferred. The contents of the string referenced by tmp2 can then be used to overwrite the address, resulting in an arbitrary code execution vulnerability.

Compliant Solution (calloc())

To correct this error, ensure the pointer returned by calloc() is not null:

Code Block
bgColor#ccccff
langc
#include <stdlib.h>
#include <string.h>

enum { SIG_DESC_SIZE = 32 };

typedef struct {
  char sig_desc[SIG_DESC_SIZE];
} signal_info;
 
void func(size_t num_of_records, size_t temp_num,
          const char *tmp2, size_t tmp2_size_bytes) {
  signal_info *start = (signal_info *)calloc(num_of_records,
                                           sizeof(signal_info));
  if (start == NULL) {
    /* Handle allocation error */
  } else if (tmp2 == NULL) {
    /* Handle error */
  } else if (temp_num > num_of_records || temp_num == 0) {
    /* Handle error */
  } else if (tmp2_size_bytes < SIG_DESC_SIZE) {
    /* Handle error */
  }

  signal_info *

...

Compliant Solution (setlocale())

A compliant solution checks the value returned by setlocale() and avoids calling mbstowcs() if the function fails. The function also takes care to restore the locale to its initial setting before returning control to the caller.

Code Block
bgColor#ccccFF
langc
size_t utf8_to_ucs(wchar_t *ucs, size_t n, const char *utf8) {
  const char *save;
  save = setlocale(LC_CTYPE, "en_US.UTF-8");
  if (NULL == save) {
    /* Propagate error to caller */
    return (size_t)-1;
  }

  n = mbstowcs(ucs, utf8, n);
  if (NULL == setlocale(LC_CTYPE, save))
    n = -1;

  return n;
}

...

Noncompliant Code Example (signal())

In this noncompliant example, the function signal() is invoked to install a handler for the SIGINT signal. signal() returns a pointer to the previously installed handler on success and the value SIG_ERR on failure. However, because the caller fails to check for errors, when signal() fails, the function may proceed with the lengthy computation without the ability to interrupt it.

Code Block
bgColor#FFcccc
langc
volatile sig_atomic_t interrupted;

void handle_interrupt(int signo) {
  interrupted = 1;
}

int f() {
  int result = 0;

  signal(SIGINT, handle_interrupt);

  while (0 == result && 0 == interrupted) {
    /* Perform a lengthy computation */
  }

  /* Indicate success or failure */
  return interrupted ? -1 : result;
}

...

Compliant Solution (signal())

A compliant solution checks the value returned by the signal() function and avoids performing the lengthy computation when signal() fails. The calling function also takes care to restore the disposition for the SIGINT signal to its initial setting before returning control to the caller.

Code Block
bgColor#ccccFF
langc
volatile sig_atomic_t interrupted;

void handle_interrupt(int signo) {
  interrupted = 1;
}

int f() {
  int result = 0;
  void (*saved_handler)(int);
  saved_handler = signal(SIGINT, handle_interrupt);

  if (SIG_ERR == saved_handler) {
    /* Indicate failure */
    return -1;
  }

  while (0 == result && 0 == interrupted) {
    /* Perform a lengthy computation */
  }

  if (SIG_ERR == signal(SIGINT, saved_handler))
    return -1;

  /* Indicate success or failure */
  return interrupted ? -1 : result;
}

...

Noncompliant Code Example (malloc())

In this noncompliant code example, temp_num, tmp2, and num_of_records are under the control of a malicious user. The attacker can easily cause malloc() to fail by providing a large value for num_of_records

Code Block
bgColor#FFCCCC
langc
signal_info * start = malloc(num_of_records * sizeof(signal_info));
signal_info * point = (signal_info *)start;
point = start + temp_num - 1; 
  memcpy(point->sig_desc, tmp2, strlen(tmp2))SIG_DESC_SIZE);
/* ... */  point->sig_desc[SIG_DESC_SIZE - 1] = '\0';
  /* ... */
  free(start);
}

Noncompliant Code Example (realloc())

This noncompliant code example calls realloc() to resize the memory referred to by p. However, if realloc() failsWhen malloc() fails, it returns a null pointer that is assigned to start. If start is a null, an attacker can provide a value for temp_num that, when scaled by the the sizeof signal_info, references a writable address to which control is eventually transferred. The contents of the string referenced by tmp2 can then be used to overwrite the address, resulting in an arbitrary code execution vulnerability.

Compliant Solution (malloc())

To correct this error, ensure the pointer returned by malloc() is not null. This also ensures compliance with MEM32-C. Detect and handle memory allocation errors.

and the connection between the original block of memory and p is lost, resulting in a memory leak.

Code Block
bgColor#FFcccc
langc
#include <stdlib.h>
 
void *p;
void func(size_t new_size) {
  if (new_size == 0) {
  
Code Block
bgColor#ccccff
langc
signal_info * start = malloc(num_of_records * sizeof(signal_info));
if (start == NULL) {
  /* Handle Allocation Errorerror */
  }
signal_info * point  p = realloc(signal_info *)startp, new_size);
point = startif + temp_num - 1; 
memcpy(point->sig_desc, tmp2, strlen(tmp2));
/* ... */ 

...

(p == NULL) {
   /* Handle error */
  }
}

This code example complies with MEM04-C. Do not perform zero-length allocations.

Compliant Solution (realloc

...

())

In this noncompliant code example, input_string is copied into dynamically allocated memory referenced by str. However, the result of malloc() is not checked before str is referenced. Consequently, if malloc() fails, the program has undefined behavior. (See undefined behavior 109 in Annex J of the C Standard.) In practice, an abnormal termination of the process typically occurs, providing an opportunity for a denial-of-service attack. In some cases, it may be the source of other vulnerabilities, as well. (See the Related Vulnerabilities section.) See also MEM32-C. Detect and handle memory allocation errors.compliant solution, the result of realloc() is assigned to the temporary pointer q and validated before it is assigned to the original pointer p:

Code Block
bgColor#ccccff
langc
#include <stdlib.h>
 
void *p;
void func(size_t new_size) {
  void *q;

  if (new_size == 0) {
    /* Handle error */
  }
 
  q = realloc(p, new_size);
  if (q == NULL
Code Block
bgColor#FFcccc
langc
void f(char *input_string) {
  size_t size/* = strlen(input_string) + 1;Handle error */
  char *str = (char *)malloc(size);
  strcpy(str, input_string);
  /* ... */
}

...

} else {
    p = q;
  }
}

Noncompliant Code Example (fseek())

In this noncompliant code example, the fseek() function is used to set the file position to a location offset in the file referred to by file prior to reading a sequence of bytes from the file. However, if an I/O error occurs during the seek operation, the subsequent read will fill the buffer with the wrong contents

...

Compliant Solution (malloc())

The malloc() function, as well as the other memory allocation functions, returns either a null pointer or a pointer to the allocated space. Always test the returned pointer to ensure it is not NULL before referencing the pointer. Handle the error condition appropriately when the returned pointer is NULL. When recovery from the allocation failure is not possible, propagate the failure to the caller.

Code Block
bgColor#ccccff#ffcccc
langc
int f(char *input_string) {
  #include <stdio.h>
 
size_t size = strlen(input_string) + 1;read_at(FILE *file, long offset,
  char *str = (char *)malloc(size);
  if (str == NULL) {
   void /*buf, Handle allocation failure and return error status */
    return -1;
  }
  strcpy(str, input_string);
  /* ... */
  free(str);
  return 0;
}

Noncompliant Code Example (realloc())

This noncompliant code example calls realloc() to resize the memory referred to by p. However, if realloc() fails, it returns a null pointer. Consequently, the connection between the original block of memory and p is severed, resulting in a memory leak.

Code Block
bgColor#FFcccc
langc
void *p;
size_t new_size = /* nonzero size */;

p = realloc(p, new_size);
if (p == NULL)   {
 /* Handle error */
}

When using realloc(), it is important to account for zero-byte allocations. (See MEM04-C. Do not perform zero-length allocations.)

...

Compliant Solution (realloc())

In this compliant solution, the result of realloc() is assigned to the temporary pointer q and validated before it is assigned to the original pointer p.

Code Block
bgColor#ccccff
langc
void *p;
void *q;
size_t new_size= /* nonzero size */;

q = realloc(p, new_size);
if (q == NULL)   {
 /* Handle error */
}
else {
  p = q;
}

Noncompliant Code Example (fseek)

In this noncompliant code example, the fseek() function is used to set the file position to a location offset in the file referred to by file prior to reading a sequence of bytes from the file. However, if an I/O error occurs during the seek operation the subsequent read will read will fill the buffer with the wrong contents.

Code Block
bgColor#ffcccc
langc
size_t read_at(FILE *file, long offset, void *buf, size_t nbytes) {
  fseek(file, offset, SEEK_SET);
  return fread(buf, 1, nbytes, file);
}

Compliant Solution (fseek)

According to the C standard, the fseek() function returns a non-zero value to indicate that an error occurred [ISO/IEC 9899:2011]. Testing for this condition before proceeding to read from the file eliminates the chance of operating on the wrong portion of the file if fseek() failed. Always test the returned value to make sure an error did not occur before operating on the file. If an error does occur, handle it appropriately.

Code Block
bgColor#ccccff
langc
size_t read_at(FILE *file, long offset, void *buf, size_t nbytes) {
  if (fseek(file, offset, SEEK_SET) != 0) {
    /* Indicate error to caller. */
    return 0;
  }
  return fread(buf, 1, nbytes, file);
}

Noncompliant Code Example (snprintf)

In the following noncompliant code example, snprinf() is assumed to succeed. However, if the call fails (for example because of insufficient memory, as described in GNU libc bug 441945), the subsequent call to log_message() is likely to result in undefined behavior since the character buffer is not initialized and need not be NUL terminated.

Code Block
bgColor#ffcccc
langc
extern void log_message(const char*);

void f(int i, int width, int prec) {
  char buf[40];
  snprintf(buf, sizeof buf, "i = %*.*i", width, prec, i);
  log_message(buf);
  /* ... */
}

Compliant Solution (snprintf)

A compliant solution avoids assuming that snprintf() succeeds regardless of its arguments and tests the return value of the function before using the buffer the function was passed to format output into. In addition, a compliant solution takes care to NUL-terminate the formatted string in case the provided buffer wasn't large enough for snprintf() to append the terminating NUL.

Code Block
bgColor#ccccff
langc
extern void log_message(const char*);

void f(int i, int width, int prec) {
  char buf[40];
  int n;
  n = snprintf(buf, sizeof buf, "i = %*.*i", width, prec, i);
  if (n < 0) {
    /* Handle snprintf() error */
    strcpy(buf, "unknown error");
  }

  /* NUL-terminate buffer in case of overflow */
  buf[sizeof buf - 1] = '\0';    
  log_message(buf);
}

When the length of the formatted string is unbounded, it is best to invoke the function twice, once with a NULL buffer to determine the size of output, then dynamically allocate a buffer of sufficient size, and finally call snprintf() again to format the output into the dynamically allocated buffer. Even with this approach the success of all calls still needs to be tested, and any errors still need to be appropriately handled. A possible optimization is to first attempt to format the string into a reasonably small buffer allocated on the stack and only when the buffer turns out to be too small allocate one of a sufficient size dynamically. This approach is shown in the compliant solution below.

Code Block
bgColor#ccccff
langc
void f(int i, int width, int prec) {
  char buffer[20];
  char *buf = buffer;
  int n  = sizeof buffer;
  const char fmt[] = "i = %*.*i";

  n = snprintf(buf, n, fmt, width, prec, i);
  if (n < 0) {
    /* Handle snprintf() error */
    strcpy(buffer, "unknown error");
    goto write_log;
  }

  if (n < sizeof buffer)
    goto write_log;

  buf = (char*)malloc(n + 1);
  if (NULL == buf) {
    /* Handle malloc() error */
    strcpy(buffer, "unknown error");
    goto write_log;
  }

  n = snprintf(buf, n, fmt, width, prec, i);
  if (n < 0) {
    /* Handle snprintf() error */
    strcpy(buffer, "unknown error");
  }

write_log:
  log_message(buf);

  if (buf != buffer)
    free(buf);
}

Note that this solution correctly handles memory allocation errors in accordance with MEM32-C. Detect and handle memory allocation errors and uses the goto statement as suggested in MEM12-C. Consider using a Goto-Chain when leaving a function on error when using and releasing resources.

Exceptions

EXP12-EX1: The exception from EXP12-C. Do not ignore values returned by functions still applies. If the return value is inconsequential or if any errors can be safely ignored, such as for functions called because of their side effects, the function should be explicitly cast to void to signify programmer intent. For an example of this exception, see "Compliant Solution (Remove Existing Destination File)" under "Portable Behavior" in FIO10-C. Take care when using the rename() function.

ERR33-EX1: Ignore the return value of a function that cannot fail or whose return value cannot signify an error condition need not be diagnosed. For example, strcpy() is one such function.

Return values from the following functions do not need to be checked because their historical use has overwhelmingly omitted error checking, and the consequences are not relevant to security.

Function

Successful Return

Error Return

printf()

number of characters (nonnegative)

negative

putchar()

character written

EOF

puts()

nonnegative

EOF (negative)

putwchar()

wide character written

WEOF

vprintf()

number of characters (nonnegative)

negative

vwprintf()

number of wide characters (nonnegative)

negative

wprintf()

number of wide characters (nonnegative)

negative

Risk Assessment

Failing to detect error conditions can lead to unpredictable results, including abnormal program termination and denial-of-service attacks or, in some situations, could even allow an attacker to run arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR33-C

high

likely

medium

P18

L1

Automated Detection

...

Tool

...

Version

...

Checker

...

Description

...

Coverity

...

CHECKED_RETURN

...

Finds inconsistencies in how function call return values are handled. Coverity Prevent cannot discover all violations of this recommendation, so further verification is necessary.

size_t nbytes) {
  fseek(file, offset, SEEK_SET);
  return fread(buf, 1, nbytes, file);
}

Compliant Solution (fseek())

According to the C Standard, the fseek() function returns a nonzero value to indicate that an error occurred. This compliant solution tests for this condition before reading from a file to eliminate the chance of operating on the wrong portion of the file if fseek() fails:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
size_t read_at(FILE *file, long offset,
               void *buf, size_t nbytes) {
  if (fseek(file, offset, SEEK_SET) != 0) {
    /* Indicate error to caller */
    return 0;
  }
  return fread(buf, 1, nbytes, file);
}

Noncompliant Code Example (snprintf())

In this noncompliant code example, snprintf() is assumed to succeed. However, if the call fails (for example, because of insufficient memory, as described in GNU libc bug 441945), the subsequent call to log_message() has undefined behavior because the character buffer is uninitialized and need not be null-terminated.

Code Block
bgColor#ffcccc
langc
#include <stdio.h>
 
extern void log_message(const char *);

void f(int i, int width, int prec) {
  char buf[40];
  snprintf(buf, sizeof(buf), "i = %*.*i", width, prec, i);
  log_message(buf);
  /* ... */
}

Compliant Solution (snprintf())

This compliant solution does not assume that snprintf() will succeed regardless of its arguments. It tests the return value of snprintf() before subsequently using the formatted buffer. This compliant solution also treats the case where the static buffer is not large enough for snprintf() to append the terminating null character as an error.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <string.h>
 
extern void log_message(const char *);

void f(int i, int width, int prec) {
  char buf[40];
  int n;
  n = snprintf(buf, sizeof(buf), "i = %*.*i", width, prec, i);
  if (n < 0 || n >= sizeof(buf)) {
    /* Handle snprintf() error */
    strcpy(buf, "unknown error");
  }
  log_message(buf);
}

Compliant Solution (snprintf(null))

If unknown, the length of the formatted string can be discovered by invoking snprintf() with a null buffer pointer to determine the size required for the output, then dynamically allocating a buffer of sufficient size, and finally calling snprintf() again to format the output into the dynamically allocated buffer. Even with this approach, the success of all calls still needs to be tested, and any errors must be appropriately handled. A possible optimization is to first attempt to format the string into a reasonably small buffer allocated on the stack and, only when the buffer turns out to be too small, dynamically allocate one of a sufficient size:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
extern void log_message(const char *); 
 
void f(int i, int width, int prec) {
  char buffer[20];
  char *buf = buffer;
  int n  = sizeof(buffer);
  const char fmt[] = "i = %*.*i";

  n = snprintf(buf, n, fmt, width, prec, i);
  if (n < 0) {
    /* Handle snprintf() error */
    strcpy(buffer, "unknown error");
    goto write_log;
  }

  if (n < sizeof(buffer)) {
    goto write_log;
  }

  buf = (char *)malloc(n + 1);
  if (NULL == buf) {
    /* Handle malloc() error */
    strcpy(buffer, "unknown error");
    goto write_log;
  }

  n = snprintf(buf, n, fmt, width, prec, i);
  if (n < 0) {
    /* Handle snprintf() error */
    strcpy(buffer, "unknown error");
  }

write_log:
  log_message(buf);

  if (buf != buffer) {
    free(buf);
  }
}

This solution uses the goto statement, as suggested in MEM12-C. Consider using a goto chain when leaving a function on error when using and releasing resources.

Exceptions

ERR33-C-EX1: It is acceptable to ignore the return value of a function if:

  • that function cannot fail.
  • its return value is inconsequential; that is, it does not indicate an error.
  • it is one of a handful of functions whose return values are not traditionally checked.

These functions are listed in the following table:

Functions for which Return Values Need Not Be Checked

Function

Successful Return

Error Return

putchar()

Character written

EOF

putwchar()

Wide character written

WEOF

puts()

Nonnegative

EOF (negative)

putws()

Nonnegative

WEOF

printf(), vprintf()

Number of characters (nonnegative)

Negative

wprintf(), vwprintf()

Number of wide characters (nonnegative)

Negative

kill_dependency()The input parameter NA
memcpy(), wmemcpy()The destination input parameterNA
memmove(), wmemmove()The destination input parameter NA
strcpy(), wcscpy()The destination input parameter NA
strncpy(), wcsncpy()The destination input parameter NA 
strcat(), wcscat()The destination input parameter NA 
strncat(), wcsncat()The destination input parameter NA
memset(), wmemset()The destination input parameterNA 

The return value of a call to fprintf() or one of its variants (vfprintf(), wfprintf(), vwfprintf()) or one of the file output functions fputc(), fputwc(), fputs(), fputws() may be ignored if the output is being directed to stdout or stderr . Otherwise, the return value must be checked.

If a function's return value is to be ignored, it is recommended that the function's return value should be explicitly cast to void to signify the programmer's intent:

Code Block
bgColor#ccccff
langc
int main() {
  (void) fprintf(stdout, "Hello, world\n"); // fprintf() return value safely ignored
}

Risk Assessment

Failing to detect error conditions can lead to unpredictable results, including abnormal program termination and denial-of-service attacks or, in some situations, could even allow an attacker to run arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR33-C

High

Likely

Medium

P18

L1

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V
error-information-unused
error-information-unused-computed
Partially checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-ERR33
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.FUNCS.IRV
LANG.ERRCODE.NOTEST
LANG.ERRCODE.NZ

Ignored return value
Missing Test of Error Code
Non-zero Error Code
Compass/ROSE

Can detect violations of this recommendation when checking for violations of EXP12-C. Do not ignore values returned by functions and EXP34-C. Do not dereference null pointers

Coverity
Include Page
Coverity_V
Coverity_V

MISRA C 2012 Rule 22.8

MISRA C 2012 Rule 22.9

MISRA C 2012 Rule 22.10

Implemented
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C3200

C++3802, C++3803, C++3804

DF2820, DF2821, DF2822, DF2823, DF2824, DF2930, DF2931, DF2932, DF2933, DF2934


Klocwork
Include Page
Klocwork_V
Klocwork_V

NPD.CHECK.MUST
NPD.FUNC.MUST
SV.RVT.RETVAL_NOTTESTED


LDRA tool suite
Include Page
LDRA_V
LDRA_V

80 D

Partially implemented
Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-ERR33-a
CERT_C-ERR33-b
CERT_C-ERR33-d

The value returned by a standard library function that may return an error should be used
The standard library functions for which return values need not be checked should be cast to 'void'
Always check the returned value of non-void function

Parasoft Insure++

Runtime analysis
PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

534

Partially supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule ERR33-C


Checks for:

  • Errno not checked
  • Return value of a sensitive function not checked
  • Unprotected dynamic memory allocation

Rule partially covered.

RuleChecker

Include Page
RuleChecker_V
RuleChecker_V

error-information-unusedPartially checked
TrustInSoft Analyzer

Include Page
TrustInSoft Analyzer_V
TrustInSoft Analyzer_V

pointer arithmeticExhaustively verified.

Related Vulnerabilities

The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from calloc(). Even when calloc() returns a null pointer, Flash writes to an offset from the return value. Dereferencing a null pointer usually results in a program crash, but dereferencing an offset from a null pointer allows an exploit to succeed without crashing the program.

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT C Secure Coding Standard

...

Compass/ROSE

...

Can detect violations of this recommendation when checking for violations of EXP12-C. Do not ignore values returned by functions and EXP34-C. Do not dereference null pointers..

...

LDRA tool suite

...

80 D

...

3200

...

Fortify SCA

...

V. 5.0

...

 

...

Related Coding Practices

Related Vulnerabilities

The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from calloc(). Even when calloc() returns NULL, Flash writes to an offset from the return value. Dereferencing NULL usually results in a program crash, but dereferencing an offset from NULL allows an exploit to succeed without crashing the program.

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

CERT C++ Secure Coding Standard: ERR10-CPP. Check for error conditions

CERT C++ Secure Coding StandardFIO04-CPP. Detect and handle input and output errors

MITRE CWE: CWE-252, "Unchecked return value"

MITRE CWE: CWE-253, "Incorrect check of function return value"

MITRE CWE: CWE-390, "Detection of error condition without action"

MITRE CWE: CWE-391, "Unchecked error condition"

ISO/IEC 9899:2011 Sections 7.21.3, "Files," 7.21.4, "Operations on files," and 7.21.9, "File positioning functions"

ISO/IEC 9899:2011 Section 7.22.3, "Memory management functions"

ISO/IEC TR 17961 (Draft) Failing to detect and handle standard library errors [liberr]

Bibliography

...

Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding StandardEXP34-C. Do not dereference null pointersPrior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding StandardFIO13-C. Never push back anything other than one read characterPrior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding StandardMEM04-C. Do not perform zero-length allocationsPrior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding StandardMEM12-C. Consider using a goto chain when leaving a function on error when using and releasing resourcesPrior to 2018-01-12: CERT: Unspecified Relationship
CERT CERR10-CPP. Check for error conditionsPrior to 2018-01-12: CERT: Unspecified Relationship
CERT CFIO04-CPP. Detect and handle input and output errorsPrior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013Failing to detect and handle standard library errors [liberr]Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11CWE-252, Unchecked Return Value2017-07-06: CERT: Partial overlap
CWE 2.11CWE-253, Incorrect Check of Function Return Value2017-07-06: CERT: Partial overlap
CWE 2.11CWE-391, Unchecked Error Condition2017-07-06: CERT: Rule subset of CWE

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-252/CWE-253/CWE-391 and ERR33-C/POS34-C

Independent( ERR33-C, POS54-C, FLP32-C, ERR34-C) Intersection( CWE-252, CWE-253) = Ø CWE-391 = Union( CWE-252, CWE-253) CWE-391 = Union( ERR33-C, POS34-C, list) where list =


  • Ignoring return values of functions outside the C or POSIX standard libraries


Bibliography

[DHS 2006]Handle All Errors Safely
[Henricson 1997]Recommendation 12.1, "Check for All Errors Reported from Functions"
[ISO/IEC 9899:2024]Subclause 7.31.3.10, "The ungetc Function"
[VU#159523]


...

Image Modified Image Modified Image Modified