CertC-INT15¶
Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types
Required inputs: IR
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:
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
intmax_t and
uintmax_t types can represent any value representable by
any other integer types of the same signedness. (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:
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 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
intmax_t or
uintmax_t. 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.
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:
#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:
#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
#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:
#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.
#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 |
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 |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
use_intmax_t |
Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types. |
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
allow_gnu_extensions¶
allow_gnu_extensions : bool = True