Few programmers consider the issues around formatted I/O and typedefs. A programmer-defined integer type might be any type supported by the implementation, even a type larger than unsigned long long
.
For example, an implementation that supports 128-bit unsigned integers and provides a uint_fast128_t
type for them would allow a programmar to use them:
typedef uint_fast128_t mytypedef_t;
Furthermore, the definition of user defined types may change. 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 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 programmer-defined integer types. Convert signed programmer-defined integer types to intmax_t
and unsigned programmer-defined integer types to uintmax_t
, then output using the j
length modifier. Similarly, input programmer-defined integer types into variables of intmax_t
or uintmax_t
(whichever matches the signedness of the programmer-defined integer type) and then convert to the programmer-defined integer types using appropriate range checks.
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 might not bother to provide format length modifiers for the type. Such a machine would still have to have a 64-bit long long
, with intmax_t
being at least that large. So, this solution can be applied even if there are no format length modifiers for the 48-bit integers.
Non-Compliant Code Example (printf()
)
The following non-compliant code example prints the value of x
, whose type is a programmer-defined integer.
#include <stdio.h> /* ... */ mytypedef_t x; /* ... */ printf("%llu", (unsigned long long) x);
However, there is no guarantee that this code prints the correct value of x
, as x
is not necessarily typed as unsigned long long
and may have a value too large to represent as an unsigned long long
.
Compliant Solution (printf()
)
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);
Non-Compliant Code Example (scanf()
)
The following non-compliant code example reads data from standard input into a programmer-defined integer.
#include <stdio.h> /* ... */ mytypedef_t x; /* ... */ if(scanf("%llu", &x) != 1) { /* handle error */ }
However, this code could result in a buffer overflow, if the size of mytypedef_t
is smaller than unsigned long long
, or it may result in an incorrect value if the size of mytypedef_t
is larger than unsigned long long
.
Compliant Solution (scanf()
)
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
.
#include <stdio.h> #include <inttypes.h> /* ... */ mytypedef_t x; uintmax_t temp; /* ... */ if(scanf("%ju", &temp) != 1) { /* handle error */ } if (temp > MYTYPEDEF_MAX) { /* handle error */ } 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-A |
high |
probable |
medium |
P12 |
L1 |
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"
INT14-A. Avoid performing bitwise and arithmetic operations on the same data 04. Integers (INT)