You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 19 Next »

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 programmer-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 programmer-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.

This rule is closely related to INT00-A. Understand the data model used by your implementation(s).

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())

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.

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);

There is no requirement that an implementation provides 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 (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)      

  • No labels