Avoid in-band error indicators while designing interfaces. This practice is commonly used by C library functions but is not recommended. One example from the C standard of a troublesome in-band error indicator is EOF
(see FIO34-C. Use int to capture the return value of character IO functions and FIO35-C. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char)). Another problematic use of in-band error indicators from the C standard involving the size_t
and time_t
types is described by MSC31-C. Ensure that return values are compared against the proper type.
Noncompliant Code Example (sprintf()
This specific noncompliant code example is from the Linux Kernel Mailing List archive site, although similar examples are common.
...
Code Block | ||
---|---|---|
| ||
int i; rsize_t count = 0; errno_t err; for (i = 0; i < 9; ++i) { err = sprintf_m( buf + count, "%02x ", &count, ((u8 *)&slreg_num)[i] ); if (err != 0) { /* Handle print error */ } } err = sprintf_m( buf + count, "%02x ", &count, ((u8 *)&slreg_num)[i] ); if (err != 0) { /* Handle print error */ } |
Exceptions
ERR02-EX1: Null pointers are another example of an in-band error indicator. Use of null pointers is not quite as bad because it is supported by the language. According to C99 Section 6.3.2.3:
If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
ERR02-EX2: You may use a function returning in-band error indicators if you can securely guarantee the program will not try to continue processing should an error occur in the function.
For example, the functions defined in TR24731-1 provide hooks for internal constraint violations. If a constraint violation handler is guaranteed not to return upon an error occurring, then you may safely ignore errors returned by these functions. You might accomplish this by having the constraint-violation handler call abort()
, or longjmp()
, for instance.
Noncompliant Code Example (POSIX ssize_t
)
The ssize_t
data type is designed as a "signed representation of size_t
". Consequently, it is often used as a return type for functions that can return an unsigned value upon success and a negative value upon error. For instance, the POSIX read()
function has the following signature:
Code Block | ||
---|---|---|
| ||
ssize_t read(int fildes, void *buf, size_t nbyte);
|
It returns -1 if an error occurs, or returns the number of bytes actually read if no errors occur.
As with all in-band error indicators, this type is not recommended. This is because developers are tempted to ignore the possibility that a ssize_t value is negative.
Compliant Solution (POSIX size_t
)
An alternative hypothetical signature for the read()
function would be:
Code Block | ||
---|---|---|
| ||
errno_t read(int fildes, void *buf, size_t nbyte, size_t* rbytes);
|
where rbytes
is a pointer to a size_t. If no error occurs, and rbytes is not NULL, its value is set to the total number of bytes read, and read()
would return 0. If an error occurs, read()
returns a nonzero value indicating the errorSee ERR03-C. Use runtime-constraint handlers when calling functions defined by TR24731-1 for more on the functions defined in TR24731-1.
Noncompliant Code Example (TR24731-1)
...
Code Block | ||
---|---|---|
| ||
/* * The abort_handler_s() function writes a message on the * standard error stream and then calls the abort() function. */ set_constraint_handler(abort_handler_s); /*...*/ /* Returns zero on success */ errno_t function(char *dst1){ char src1[100] = "hello"; strcpy_s(dst1, sizeof(dst1), src1); /* Because abort_handler_s() never returns, we only get here if strcpy_s() succeeds. */ /* ... */ return 0; } |
Exceptions
ERR02-EX1: Null pointers are another example of an in-band error indicator. Use of null pointers is not quite as bad because it is supported by the language. According to C99 Section 6.3.2.3:
If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
ERR02-EX2: You may use a function returning in-band error indicators if you can securely guarantee the program will not try to continue processing should an error occur in the function.
For example, the functions defined in TR24731-1 provide hooks for internal constraint violations. If a constraint violation handler is guaranteed not to return upon an error occurring, then you may safely ignore errors returned by these functions. You might accomplish this by having the constraint-violation handler call abort()
, or longjmp()
, for instance.
See ERR03-C. Use runtime-constraint handlers when calling functions defined by TR24731-1 for more on the functions defined in TR24731-1.
Risk Assessment
The risk in using in-band error indicators is difficult to quantify and is consequently given as low. However, if the use of in-band error indicators results in programmers' failing to check status codes or incorrectly checking them, the consequences can be more severe.
...