Few programmers consider the issues around formatted I/O and typedefs. A user-defined integer type might be any type supported by the implementation, even a type larger than unsigned long long
, for example:
typdef mytypedef_t uint_fast128_t;
Furthermore, the definition of user 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 with formatted output functions (such as printf()
) and formatted input functions (such as scanf()
) (see FIO00-A. Take care when creating format strings).
The C99 intmax_t
and uintmax_t
types are capable of representing any value representable by any other integer types of the same signedness. This allows conversion between user defined integer types (of the same signedness) and intmax_t
and uintmax_t
. For example:
mytypedef_t x; uintmax_t temp; /* ... */ temp = x; /* always safe */ if (temp <= MYTYPEDEF_MAX) x = temp;
Formatted input and output functions contain a length modifier which provides the above facilities for input/output. 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 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 user-defined integer types of the same signedness to intmax_t
and uintmax_t
and then output using the j
length modifier. Similarly, input user defined types of the same signedness into variables of intmax_t
and uintmax_t
and then convert to the user-defined integer types using appropriate range checks.
Similarly, 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 not bother to provide format length modifiers for the type. Such a machine would still have to have a 64-bit long long, and 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.
Non-Compliant Code Example (printf()
)
There is no guarantee that this non-compliant code example prints the correct value of x
.
mytypedef_t x; printf("%lu", x);
Compliant Solution (printf()
)
This compliant solution uses the correct format for the type being used.
mytypedef_t x; printf("%ju", (uintmax_t)x);
Non-Compliant Code Example (scanf()
)
There is no guarantee that this non-compliant code example prints the correct value of x
.
mytypedef_t x; scanf("%lu", &x);
Compliant Solution (scanf()
)
This compliant solution uses the correct format for the type being used.
mytypedef_t x; uintmax_t temp; scanf("%ju", &temp); if (temp > MYTYPEDEF_MAX) { /* handle error */ } x = temp;
Risk Assessment
Failure to
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
INT15-A |
low |
unlikely |
high |
P1 |
L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[ISO/IEC 9899-1999]] Section 7.18.1.5, "Greatest-width integer types," and Section 7.19.6, "Formatted input/output functions"
04. Integers (INT) 04. Integers (INT) INT01-A. Use rsize_t or size_t for all integer values representing the size of an object