Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: formatting and implementation details

...

Function

Successful Return

Error Return

aligned_alloc()

pointer to space

NULL

asctime_s()

zero

nonzero

at_quick_exit()

zero

nonzero

atexit()

zero

nonzero

bsearch()

pointer to matching element

NULL

bsearch_s()

pointer to matching element

NULL

btowc()

converted wide character

WEOF

c16rtomb()

number of bytes

(size_t)(-1)

c32rtomb()

number of bytes

(size_t)(-1)

calloc()

pointer to space

NULL

clock()

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

nonzero

fclose()

zero

EOF (negative)

fflush()

zero

EOF (negative)

fgetc()

character read

EOF2

fgetpos()

zero

nonzero

fgets()

pointer to string

NULL

fgetwc()

wide character read

WEOF2

fopen()

pointer to stream

NULL

fopen_s()

zero

nonzero

fprintf()

number of characters (nonnegative)

negative

fprintf_s()

number of characters (nonnegative)

negative

fputc()

character written

EOF1

fputs()

nonnegative

EOF (negative)

fputws()

nonnegative

EOF (negative)

fread()

elements read

elements read

freopen()

pointer to stream

NULL

freopen_s()

zero

nonzero

fscanf()

number of conversions (nonnegative)

EOF (negative)

fscanf_s()

number of conversions (nonnegative)

EOF (negative)

fseek()

zero

nonzero

fsetpos()

zero

nonzero

ftell()

file position

−1L

fwprintf()

number of wide characters (nonnegative)

negative

fwprintf_s()

number of wide characters (nonnegative)

negative

fwrite()

elements written

elements written

fwscanf()

number of conversions (nonnegative)

EOF (negative)

fwscanf_s()

number of conversions (nonnegative)

EOF (negative)

getc()

character read

EOF2

getchar()

character read

EOF2

getenv()

pointer to string

NULL

getenv_s()

pointer to string

NULL

gets_s()

pointer to string

NULL

getwc()

wide character read

WEOF

getwchar()

wide character read

WEOF

gmtime()

pointer to broken-down time

NULL

gmtime_s()

pointer to broken-down time

NULL

localtime()

pointer to broken-down time

NULL

localtime_s()

pointer to broken-down time

NULL

malloc()

pointer to space

NULL

mblen(), s != NULL

number of bytes

−1

mbrlen(), s != NULL

number of bytes or status

(size_t)(-1)

mbrtoc16()

number of bytes or status

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

mbrtoc32()

number of bytes or status

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

mbrtowc(), s != NULL

number of bytes or status

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

mbsrtowcs()

number of non-null elements

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

mbsrtowcs_s()

zero

nonzero

mbstowcs()

number of non-null elements

(size_t)(-1)

mbstowcs_s()

zero

nonzero

mbtowc(), s != NULL

number of bytes

−1

memchr()

pointer to located character

NULL

mktime()

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 of characters (nonnegative)

negative

putc()

character written

EOF1

putwc()

wide character written

WEOF

raise()

zero

nonzero

realloc()

pointer to space

NULL

remove()

zero

nonzero

rename()

zero

nonzero

setlocale()

pointer to string

NULL

setvbuf()

zero

nonzero

scanf()

number of conversions (nonnegative)

EOF (negative)

scanf_s()

number of conversions (nonnegative)

EOF (negative)

signal()

pointer to previous function

SIG_ERR, errno > 0

snprintf()

number of characters that would be written (nonnegative)

negative

snprintf_s()

number of characters that would be written (nonnegative)

negative

sprintf()

number of non-null characters written

negative

sprintf_s()

number of non-null characters written

negative

sscanf()

number of conversions (nonnegative)

EOF (negative)

sscanf_s()

number of conversions (nonnegative)

EOF (negative)

strchr()

pointer to located character

NULL

strerror_s()

zero

nonzero

strftime()

number of non-null characters

zero

strpbrk()

pointer to located character

NULL

strrchr()

pointer to located character

NULL

strstr()

pointer to located string

NULL

strtod()

converted value

zero, errno == ERANGE

strtof()

converted value

zero, errno == ERANGE

strtoimax()

converted value

INTMAX_MAX or INTMAX_MIN, errno == ERANGE

strtok()

pointer to first character of a token

NULL

strtok_s()

pointer to first character of a token

NULL

strtol()

converted value

LONG_MAX or LONG_MIN, errno == ERANGE

strtold()

converted value

zero, errno == ERANGE

strtoll()

converted value

LLONG_MAX or LLONG_MIN, errno == ERANGE

strtoumax()

converted value

UINTMAX_MAX, errno == ERANGE

strtoul()

converted value

ULONG_MAX, errno == ERANGE

strtoull()

converted value

ULLONG_MAX, errno == ERANGE

strxfrm()

length of transformed string

>= n

swprintf()

number of non-null wide characters

negative

swprintf_s()

number of non-null wide characters

negative

swscanf()

number of conversions (nonnegative)

EOF (negative)

swscanf_s()

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

negative

time()

calendar time

(time_t)(-1)

timespec_get()

base

zero

tmpfile()

pointer to stream

NULL

tmpfile_s()

zero

nonzero

tmpnam()

non-null pointer

NULL

tmpnam_s()

zero

nonzero

tss_create()

thrd_success

thrd_error

tss_get()

value of thread-specific storage

zero

tss_set()

thrd_success

thrd_error

ungetc()

character pushed back

EOF (negative; see below)

ungetwc()

character pushed back

WEOF (negative)

vfprintf()

number of characters (nonnegative)

negative

vfprintf_s()

number of characters (nonnegative)

negative

vfscanf()

number of conversions (nonnegative)

EOF (negative)

vfscanf_s()

number of conversions (nonnegative)

EOF (negative)

vfwprintf()

number of wide characters (nonnegative)

negative

vfwprintf_s()

number of wide characters (nonnegative)

negative

vfwscanf()

number of conversions (nonnegative)

EOF (negative)

vfwscanf_s()

number of conversions (nonnegative)

EOF (negative)

vprintf_s()

number of characters (nonnegative)

negative

vscanf()

number of conversions (nonnegative)

EOF (negative)

vscanf_s()

number of conversions (nonnegative)

EOF (negative)

vsnprintf()

number of characters that would be written (nonnegative)

negative

vsnprintf_s()

number of characters that would be written (nonnegative)

negative

vsprintf()

number of non-null characters (nonnegative)

negative

vsprintf_s()

number of non-null characters (nonnegative)

negative

vsscanf()

number of conversions (nonnegative)

EOF (negative)

vsscanf_s()

number of conversions (nonnegative)

EOF (negative)

vswprintf()

number of non-null wide characters

negative

vswprintf_s()

number of non-null wide characters

negative

vswscanf()

number of conversions (nonnegative)

EOF (negative)

vswscanf_s()

number of conversions (nonnegative)

EOF (negative)

vwprintf_s()

number of wide characters (nonnegative)

negative

vwscanf()

number of conversions (nonnegative)

EOF (negative)

vwscanf_s()

number of conversions (nonnegative)

EOF (negative)

wcrtomb()

number of bytes stored

(size_t)(-1)

wcschr()

pointer to located wide character

NULL

wcsftime()

number of non-null wide characters

zero

wcspbrk()

pointer to located wide character

NULL

wcsrchr()

pointer to located wide character

NULL

wcsrtombs()

number of non-null bytes

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

wcsrtombs_s()

zero

nonzero

wcsstr()

pointer to located wide string

NULL

wcstod()

converted value

zero, errno == ERANGE

wcstof()

converted value

zero, errno == ERANGE

wcstoimax()

converted value

INTMAX_MAX or INTMAX_MIN, errno == ERANGE

wcstok()

pointer to first wide character of a token

NULL

wcstok_s()

pointer to first wide character of a token

NULL

wcstol()

converted value

LONG_MAX or LONG_MIN, errno == ERANGE

wcstold()

converted value

zero, errno == ERANGE

wcstoll()

converted value

LLONG_MAX or LLONG_MIN, errno == ERANGE

wcstombs()

number of non-null bytes

(size_t)(-1)

wcstombs_s()

zero

nonzero

wcstoumax()

converted value

UINTMAX_MAX, errno == ERANGE

wcstoul()

converted value

ULONG_MAX, errno == ERANGE

wcstoull()

converted value

ULLONG_MAX, errno == ERANGE

wcsxfrm()

length of transformed wide string

>= n

wctob()

converted character

EOF

wctomb(), s != NULL

number of bytes stored

−1

wctomb_s(), s != NULL

number of bytes stored

−1

wctrans()

valid argument to towctrans

zero

wctype()

valid argument to iswctype

zero

wmemchr()

pointer to located wide character

NULL

wprintf_s()

number of wide characters (nonnegative)

negative

wscanf()

number of conversions (nonnegative)

EOF (negative)

wscanf_s()

number of conversions (nonnegative)

EOF (negative)

When 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 as follows:

...

Anchor
ungetc
ungetc
The 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," 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.)

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

...

Anchor
nce_realloc
nce_realloc

Noncompliant Code Example (setlocale())

In this noncompliant example, the function utf8_to_ucs() attempts to convert a sequence of UTF-8 characters to UCS. It first invokes setlocale() to set the global locale to "en_US.UTF-8" but does not check for failure. setlocale() will fail by

...

Noncompliant Code Example (setlocale())

In this noncompliant example, the function utf8_to_ucs() attempts to convert a sequence of UTF-8 characters to UCS. It first invokes setlocale() to set the global locale to "en_US.UTF-8" 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#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
void f(int i, int width, int prec) {
  char buffer[20];
  char *buf = buffer;
  int n  = sizeof buffer;
  const 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);
}

...

 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.

Implementation Details

POSIX

In addition to the C standard library functions above, following is an incomplete list of functions defined in POSIX that require error checking.

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

  1. Anchor
    1
    1
     Setting errno is a POSIX [ISO/IEC 9945:2008] extension to the C standard.
  2. Anchor
    2
    2
     On error, posix_memalign() returns a value that corresponds to one of the constants defined in the <errno.h> header. The function does not set errno. The posix_memalign() function is optional and is not required to be provided by conforming implementations.

Noncompliant Code Example (POSIX)

In the following noncompliant code example, fmemopen() and open_memstream() are assumed to succeed. However, if the calls fails, the two file pointers in and out will be NULL and the program has undefined behavior.

Code Block
bgColor#ffcccc
langc
int main(int argc, char *argv[])
{
FILE *out, *in;
 
if (argc != 2) {
	/* Handle Error */
}
 
in = fmemopen(argv[1], strlen(argv[1]), "r"); /* violation */
/* Use in */
 
out = open_memstream(&ptr, &size); /* violation */
/* Use out */
 
}

Compliant Solution (POSIX)

A compliant solution avoids assuming that fmemopen() and open_memstream() succeed regardless of its arguments and tests the return value of the function before using the file pointers in and out.

Code Block
bgColor#ccccff
langc
int main(int argc, char *argv[])
{
FILE *out, *in;
 
if (argc != 2) {
	/* Handle Error */
}
 
in = fmemopen(argv[1], strlen(argv[1]), "r");


if (in == NULL){
	/* Handle Error */
}
/* Use in */
 
out = open_memstream(&ptr, &size);

if (out == NULL){
	/* Handle Error */

}
/* Use out */
 
}

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.

...

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.

...