Few programmers consider the issues around formatted I/O and typedefstype definitions. A userprogrammer-defined integer type might be any type supported by the implementation, even a type larger than unsigned long long
, for example. For example, given an implementation that supports 128-bit unsigned integers and provides a uint_fast128_t
type, a programmer may define the following type:
Code Block |
---|
typedef mytypedefuint_fast128_t uintmytypedef_fast128_t; |
Furthermore, the definition of user programmer-defined types may change (which may be one of the reasons the user-defined type was created to begin with). This creates a problem using these types , which creates a problem when these types are used with formatted output functions (, such as printf()
) , and formatted input functions (, such as scanf()
) . (see FIO00See FIO47-A. Take care when creating C. Use valid format strings.).
The C99 C intmax_t
and uintmax_t
types are capable of representing any types can represent any value representable by any other integer types of the same signedness. This allows conversion between user (See INT00-C. Understand the data model used by your implementation(s).) This capability allows conversion between programmer-defined integer types (of the same signedness) and intmax_t
and uintmax_t
. For example:
Code Block | ||
---|---|---|
| ||
mytypedef_t x; uintmax_t temp; temp = x; /* Always secure if mytypedef_t is unsigned*/ /* ... */ temp = x; /* always safeChange the value of temp ... */ if (temp <= MYTYPEDEF_MAX) { x = temp; } |
Formatted I/O functions can be used to input and output functions contain a length modifier which provides the above facilities for input/outputgreatest-width integer typed values. The j
length modifier in a format string indicates that the following d
, i
, o
, u
, x
, X
, or n
conversion specifier will apply to an argument with type pointer to intmax_t
or uintmax_t
. C99 C also specifies the z
length modifier for use with arguments of type size_t
, and the t
length modifier for arguments of type ptrdiff_t
.
The C99 intmax_t
and uintmax_t
can safely be used to perform formatted I/O with user-defined integer types. Convert signed user-defined integer types to intmax_t
and unsigned user-defined integer types to uintmax_t
then output using the j
length modifier. Similarly, input user-defined integer types into variables of intmax_t
or uintmax_t
(whichever matches the signedness of the user-defined integer type) and then convert to the user-defined integer types using appropriate range checks.
Similarly, In addition to programmer-defined types, there is no requirement that an implementation provide format-length modifiers for implementation-defined integer types. For example, a machine with a 16-bit word size with an implementation-defined 48-bit integer type might may not bother to provide format-length modifiers for the type. Such a machine would still have to have machine still must have a 64-bit long long
, and with intmax_t
would probably be that type. So, this solution can be applied even if there were no format length modifiers for the 48-bit integers.
...
being at least that large.
Noncompliant Code Example (printf()
)
There is no guarantee that this non-compliant This noncompliant code example prints the correct value of x
. as an unsigned long long
value even though the value is of a programmer-defined integer type:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> mytypedef_t x; /* ... */ printf("%llu", (unsigned long long) x); |
This code example is non-compliant for two reasons: it assumes that x
has type unsigned long long
and that unsigned long long
is large enough to represent x
There is no guarantee that this code prints the correct value of x
, as x
may be too large to represent as an unsigned long long
.
Compliant Solution (printf()
)
The C intmax_t
and uintmax_t
can be safely used to perform formatted I/O with programmer-defined integer types by converting signed programmer-defined integer types to intmax_t
and unsigned programmer-defined integer types to uintmax_t
, then outputting these values using the j
length modifier. Similarly, programmer-defined integer types can be input to variables of intmax_t
or uintmax_t
(whichever matches the signedness of the programmer-defined integer type) and then converted to programmer-defined integer types using appropriate range checks.
This compliant solution guarantees that the correct value of x
is printed, regardless of its length, provided that mytypedef_t
is an unsigned type:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <inttypes.h> mytypedef_t x; /* ... */ printf("%ju", (uintmax_t) x); |
Compliant Solution (Microsoft printf()
)
Visual Studio 2012 and earlier versions do not support the standard j
length modifier and do not have a nonstandard analog. Consequently, the programmer must hard code the knowledge that intmax_t
is int64_t
and uintmax_t
is
for Microsoft Visual Studio versions.uint64_t
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <inttypes.h> mytypedef_t x; /* ... */ #ifdef _MSC_VER printf("%llu", (uintmax_t) x); #else printf("%ju", (uintmax_t) x); |
...
#endif |
A feature request has been submitted to Microsoft to add support for the j
length modifier to a future release of Microsoft Visual Studio.
Noncompliant Code Example (scanf()
)
This noncompliant code example reads an unsigned long long
value from standard input and stores the result in x
, which is of a programmer-defined integer type:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h>
mytypedef_t x;
/* ... */
if (scanf("%llu", &x) != 1) {
/* Handle error */
}
|
This noncompliant code example can result in a buffer overflow non-compliant code example will result in a "buffer overflow", if the size of mytypedef_t
is smaller than unsigned long long
, or it may might result in an incorrect value if the size of mytypedef_t
is larger than unsigned long long
.
Code Block | ||
---|---|---|
| ||
mytypedef_t x;
scanf("%llu", &x);
|
Moreover, scanf()
lacks the error checking capabilities of alternative conversion routines, such as strtol()
. For more information, see INT06-C. Use strtol() or a related function to convert a string token to an integer.
Compliant Solution (strtoumax
...
()
)
This compliant solution guarantees that a correct value in the range of mytypedef_t
is read, or an error condition is detected, assuming the value of MYTYPEDEF_MAX
is correct as the largest value representable by mytypedef_t
: The strtoumax()
function is used instead of scanf()
as it provides enhanced error checking functionality. The fgets()
function is used to read input from stdin
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <inttypes.h> #include <errno.h> mytypedef_t x; uintmax_t temp; scanf("%ju", &temp); /* ... */ if (fgets(buff, sizeof(buff), stdin) == NULL) { if (puts("EOF or read error\n") == EOF) { /* Handle error */ } } else { /* Check for errors in the conversion */ errno = 0; temp = strtoumax(buff, &end_ptr, 10); if (ERANGE == errno) { if (puts("number out of range\n") == EOF) { /* Handle error */ } } else if (end_ptr == buff) { if (puts("not valid numeric input\n") == EOF) { /* Handle error */ } } else if ('\n' != *end_ptr && '\0' != *end_ptr) { if (puts("extra characters on input line\n") == EOF) { /* Handle error */ } } /* No conversion errors, attempt to store the converted value into x */ if (temp > MYTYPEDEF_MAX) { /* handleHandle error */ } else { x = temp; } } |
Risk Assessment
Failure to use an appropriate conversion specifier when inputting or outputting userprogrammer-defined integer types can result in buffer overflow and lost or misinterpreted data.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
INT15- |
C |
High |
Unlikely |
Medium | P6 | L2 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Axivion Bauhaus Suite |
| CertC-INT15 | |||||||
Compass/ROSE | Can catch violations of this rule by scanning the | ||||||||
LDRA tool suite |
| 586 S | Enhanced Enforcement | ||||||
Parasoft C/C++test |
| CERT_C-INT15-a | Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]] Section 7.18.1.5, "Greatest-width integer types," and Section 7.19.6, "Formatted input/output functions" |
Related Guidelines
SEI CERT C++ Coding Standard | VOID INT15-CPP. Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types |
MITRE CWE | CWE-681, Incorrect conversion between numeric types |
Bibliography
[Saks 2007c] | Standard C's Pointer Difference Type |
...
INT14-A. Avoid performing bitwise and arithmetic operations on the same data 04. Integers (INT)