CertC-FLP04¶
Check floating-point inputs for exceptional values
Required inputs: IR
Floating-point numbers can take on two classes of exceptional values; infinity
and NaN (not-a-number). These values are returned as the result of exceptional
or otherwise unresolvable floating-point operations. (See also
FLP32-C.
Prevent or detect domain and range errors in math functions.) Additionally,
they can be directly input by a user by
scanf or similar functions. Failure to detect and handle such
values can result in
undefined
behavior.
NaN values are particularly problematic because the expression NaN == NaN (for every possible value of NaN) returns false. Any comparisons made with NaN as one of the arguments returns false, and all arithmetic functions on NaNs simply propagate them through the code. Hence, a NaN entered in one location in the code and not properly handled could potentially cause problems in other, more distant sections.
Formatted-input functions such as
scanf will accept the values
INF,
INFINITY, or
NAN (case insensitive) as valid inputs for the
%f format specification, allowing malicious users to feed them
directly to a program. Programs should therefore check to ensure that all input
floating-point values (especially those controlled by the user) have neither of
these values if doing so would be inappropriate. The
<math.h> library provides two macros for this purpose:
isinf and
isnan.
isinf and
isnan
The
isinf macro tests an input floating-point value for infinity.
isinf(val) is nonzero if
val is an infinity (positive or negative), and 0 otherwise.
isnan tests if an input is NaN.
isnan(val) is nonzero if
val is a NaN, and 0 otherwise.
If infinity or NaN values are not acceptable inputs in a program, these macros should be used to ensure they are not passed to vulnerable functions.
Noncompliant Code Example
This noncompliant code example accepts user data without first validating it:
float currentBalance; /* User's cash balance */
void doDeposit() {
float val;
scanf("%f", &val);
if(val >= MAX_VALUE - currentBalance) {
/* Handle range error */
}
currentBalance += val;
}
This can be a problem if an invalid value is entered for
val and subsequently used for calculations or as control values.
The user could, for example, input the strings
"INF",
"INFINITY", or
"NAN" (case insensitive) on the command line, which would be
parsed by
scanf into the floating-point representations of infinity and NaN.
All subsequent calculations using these values would be invalid, possibly
crashing the program and enabling a
denial-of-service
attack.
Here, for example, entering
"nan" for
val would force
currentBalance to also equal
"nan", corrupting its value. If this value is used elsewhere for
calculations, every resulting value would also be a NaN, possibly destroying
important data.
Implementation Details
The following code was run on 32-bit GNU Linux using the GCC 3.4.6 compiler. On
this platform,
FLT_MAX has the value
340282346638528859811704183484516925440.000000.
#include <stdio.h>
int main(int argc, char *argv[]) {
float val, currentBalance=0;
scanf("%f", &val);
currentBalance+=val;
printf("%f\n", currentBalance);
return 0;
}
The following table shows the value of
currentBalance returned for various arguments:
| Input | currentBalance |
|---|---|
25 |
25.00000 |
infinity |
inf |
inf |
inf |
-infinity |
-inf |
NaN |
nan |
nan |
nan |
1e9999 |
inf |
-1e9999 |
-inf |
As this example demonstrates, the user can enter the exceptional values
infinity and
NaN, as well as force a float's value to be infinite, by entering
out-of-range floats. These entries subsequently corrupt the value of
currentBalance. So by entering exceptional floats, an attacker can
corrupt the program data, possibly leading to a crash.
Compliant Solution
This compliant solution first validates the input float before using it. The value is tested to ensure that it is neither an infinity nor a NaN.
float currentBalance; /* User's cash balance */
void doDeposit() {
float val;
scanf("%f", &val);
if (isinf(val)) {
/* Handle infinity error */
}
if (isnan(val)) {
/* Handle NaN error */
}
if (val >= MAX_VALUE - currentBalance) {
/* Handle range error */
}
currentBalance += val;
}
Exceptions
Occasionally, NaN or infinity may be acceptable or expected inputs to a program. If this is the case, then explicit checks may not be necessary. Such programs must, however, be prepared to handle these inputs gracefully and not blindly use them in mathematical expressions where they are not appropriate.
Risk Assessment
Inappropriate floating-point inputs can result in invalid calculations and unexpected results, possibly leading to crashing and providing a denial-of-service opportunity.
| Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| FLP04-C | Low | Probable | High | P2 | L3 |
Related Guidelines
| SEI CERT C++ Coding Standard | FLP04-CPP. Check floating-point inputs for exceptional values |
| CERT Oracle Secure Coding Standard for Java | FLP06-J. Check floating-point inputs for exceptional values |
Bibliography
| [ IEEE 754] |
| [ IEEE Std 1003.1:2013] |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
uncleared_float_value |
Check floating-point inputs for exceptional values before use. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
scanf_functions¶
scanf_functions
Members of the scanf family to check.Type: dict[bauhaus.analysis.config.QualifiedName, typing.Tuple[str, int, typing.Optional[int]]]
Default:
{ 'fscanf': ('scanf', 1, 2), 'scanf': ('scanf', 0, 1), 'sscanf': ('scanf', 1, 2), 'vfscanf': ('scanf', 1, None), 'vscanf': ('scanf', 0, None), 'vsscanf': ('scanf', 1, None) }