...
Code Block | ||
---|---|---|
| ||
if (something_really_bad_happenedhappens) { take_me_some_place_safe(); } |
...
Because application-independent code is not associated with any application, it cannot handle errors. However, it must still detect errors, and report them to an application, so that the application may still handle them.
Error detection and reporting can take several forms:
...
Non-Compliant Code Example
Suppose we have the following application-independent code, which is called externally by invoking the f()
This non-compliant code example consists of two application-independent functions f()
and g()
. The f()
function is part of the external API for the module; the g()
function is an internal function.
Code Block | ||
---|---|---|
| ||
void g(void) { /* ... */ if (something_really_bad_happenedhappens) { fprintf( stderr, "Something really bad happened!\n"); abort(); } /* ... */ } void f(void) { g(); /* ... do the rest of f ... */ } |
If something_really_bad_happens
in g()
, the function prints an error message to stderr
and then calls abort()
. The problem is that this application-independent code does not know the context in which it is being called, so it is erroneous to handle the error.
Wiki Markup |
---|
Practice 23 from the Miller, et. al paper on Software Quality Engineering (SQE) practices \[[Miller 04|AA. C References#Miller 04]\] says: |
When a library aborts due to some kind of anomaly, it is saying there is no hope for execution to proceed normally beyond the point where the anomaly is detected. Nonetheless, it is dictatorially making this decision on behalf of the client. Even if the anomaly turns out to be some kind of internal bug in the library, which obviously cannot be resolved in the current execution, aborting is a bad thing to do. The fact is, a library developer cannot possibly know the fault-tolerant context in which his/her library is being used. The client may indeed be able to recover from the situation even if the library cannot.
It is equally bad to eliminate the call to abort()
from f()
. In this case, there will be no indication back to the calling function that an error has occuredIn this code, a serious error is detected in g()
which prints the error, but does nothing to alter subsequent program behavior. The application receives no indication the error even occurred.
Compliant Solution (Return Value)
...
Code Block | ||
---|---|---|
| ||
const errno_t ESOMETHINGREALLYBAD = 1; errno_t g(void) { /* ... */ if (something_really_bad_happenedhappens) { return ESOMETHINGREALLYBAD; } /* ... */ return 0; } errno_t f(void) { errno_t status; if ((status = g()) != 0) return status; /* ... do the rest of f ... */ return 0; } |
...
Code Block | ||
---|---|---|
| ||
const errno_t ESOMETHINGREALLYBAD = 1; void g(errno_t* err) { if (err == NULL) { /* handle null pointer */ } /* ... */ if (something_really_bad_happenedhappens) { *err = ESOMETHINGREALLYBAD; } else { /* ... */ *err = 0; } } void f(errno_t* err) { if (err == NULL) { /* handle null pointer */ } g(err); if (*err == 0) { /* ... do the rest of f ... */ } return 0; } |
...
Code Block | ||
---|---|---|
| ||
errno_t my_errno; /* also declared in a .h file */ const errno_t ESOMETHINGREALLYBAD = 1; void g(void) { /* ... */ if (something_really_bad_happenedhappens) { my_errno = ESOMETHINGREALLYBAD; return; } /* ... */ } void f(void) { g(); if (my_errno != 0) { return; } /* ... do the rest of f ... */ } |
...
Code Block | ||
---|---|---|
| ||
#include <setjmp.h> const errno_t ESOMETHINGREALLYBAD = 1; jmp_buf exception_env; void g(void) { /* ... */ if (something_really_bad_happenedhappens) { longjmp(exception_env, ESOMETHINGREALLYBAD); } /* ... */ } void f(void) { g(); /* ... do the rest of f ... */ } /* ... */ errno_t err; if ((err = setjmp(exception_env)) != 0) { /* if we get here, an error occurred and err indicates what went wrong */ } /* ... */ f(); /* if we get here, no errors occurred */ /* ... */ |
...
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[Miller 04|AA. C References#Miller 04]\]
\[[Saks 07b|AA. C References#Saks 07b]\] |
...
ERR04-A. Choose an appropriate termination strategy 12. Error Handling (ERR) ERR30-C. Set errno to zero before calling a function, and use it only after the function returns a value indicating failure