Few programmers consider the issues around formatted I/O and type definitions. A programmer-defined integer type might be any type supported by the implementation, even a type larger than unsigned long long
. 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 uint_fast128_t mytypedef_t;
|
Furthermore, the definition of programmer-defined types may change, which creates a problem when these types are used with formatted output functions, such as printf()
, and formatted input functions, such as scanf()
. (See FIO47-C. Use valid format strings.)
The C
THIS IS THE VOID SECTION, ITS CONTENTS ARE NOT INTENDED TO BE PART OF THE SECURE CODING STANDARDS.
By definition, the types intmax_t
and uintmax_t
are capable of representing any types can represent any value representable by any other integer types of the same signedness. By using these types, the danger of truncation associated with the ambiguities of a change in platform or architecture, e.g. IA-32 to x86-64, can be mitigated.(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
:
Code Block | ||
---|---|---|
| ||
mytypedef_t x;
uintmax_t temp;
temp = x; /* Always secure if mytypedef_t is unsigned*/
/* ... Change the value of temp ... */
if (temp <= MYTYPEDEF_MAX) {
x = temp;
}
|
Formatted I/O functions can be used to input and output greatest-width integer typed values. The j
length modifier Formatted input and output functions contain a length modifier which provides the above facilities for input/output. The j
specifier 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
. \[[ISO/IEC 9899-1999:TC2|AA. C References#ISO/IEC 9899-1999TC2]] Section 7.19.6, "Formatted input/output functions," and Section 7.18.1.5, "Greatest-width integer types"C also specifies the Wiki Markup z
length modifier for use with arguments of type size_t
and the t
length modifier for arguments of type ptrdiff_t
.
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 an implementation-defined 48-bit integer type may not provide format-length modifiers for the type. Such a machine still must have a 64-bit long long
, with intmax_t
being at least that large.
Noncompliant Code Example (printf()
)
This noncompliant code example prints the 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);
|
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 if the size of mytypedef_t
is smaller than unsigned long long
, or it might result in an incorrect value if the size of mytypedef_t
is larger than unsigned long long
. 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;
/* ... */
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) {
/* Handle error */
} else {
x = temp;
}
} |
Risk Assessment
Failure to use an appropriate conversion specifier when inputting or outputting programmer-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.
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 |
...