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:
...
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 FIO00FIO47-C. Take care when creating 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
:
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;
}
|
...
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.
...
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 |
Microsoft A feature request has been submitted a feature request to Microsoft to add support for the j
length modifier to a future release of Microsoft Visual Studio.
Noncompliant Code Example (scanf()
)
The following 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:
...
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 (scanf("%ju", &temp) != 1(fgets(buff, sizeof(buff), stdin) == NULL) { /* Handle error */ } if (temp > MYTYPEDEF_MAXif (puts("EOF or read error\n") == EOF) { /* Handle error */ } } else { x = temp; } |
Compliant Solution (Microsoft scanf()
)
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; uintmax_t temp; /* ... */ #ifdef _MSC_VER # define UINTMAX_CS "%llu" #else # define UINTMAX_CS "%ju" #endif if (scanf(UINTMAX_CS, &temp) != 1) { /* Handle error */ } if (temp > MYTYPEDEF_MAX) { /* 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 { x} = temp; } |
Microsoft has submitted a feature request to add support for the j
length modifier to a future release of Microsoft Visual Studio.
Noncompliant Code Example (extended types)
Consider the following code sample, which indicates if an aray has room for elem_count
more elements:
Code Block | ||||
---|---|---|---|---|
| ||||
/* test if we can fit elem_count chars in the space between p_current and p_max. */ bool test_ptr(unsigned int elem_count, char *p_max, char *p_current) { int subscript_diff = p_max - p_current; if ((p_max > p_current) && (subscript_diff > elem_count) else if (end_ptr == buff) { if (puts("not valid numeric input\n") == EOF) { /* Handle error */ } } else if ('\n' != *end_ptr && '\0' != *end_ptr) { return true; } return false; } |
Consider what happens when this function is called in the following manner on a 64-bit platform:
Code Block | ||||
---|---|---|---|---|
| ||||
unsigned int char_count = (unsigned int) INT_MAX + 10; char *huge_string = malloc(char_count); /* we have lots of RAM ;) */ /* ... */ if (test_ptr(3, huge_string + char_count - 1, huge_string) {if (puts("extra characters on input line\n") == EOF) { /* Handle error */ } } /* addNo 3conversion moreerrors, elementsattempt to huge_string */ } |
In this scenario, test_ptr()
will return false when it should return true. The result of p_max - p_current
is a ptrdiff_t
with a mathematical value of INT_MAX + 10
. However, on a 64-bit platform, if int
is still 32 bits, then when p_max - p_current
is stored into an int
, the result will be a negative value of INT_MIN + 9
. Now subscript_diff
is less than elem_count
and the comparison will fail.
Compliant Solution (extended types)
In this compliant solution, we declare subscript_diff
to be a intmax_t
, which is, by definition, large enough to contain the difference between two pointers:
Code Block | ||||
---|---|---|---|---|
| ||||
/* test if we can fit elem_count chars in the space between p_current and p_max. */ bool test_ptr(unsigned int elem_count, char *p_max, char *p_currentstore the converted value into x */ if (temp > MYTYPEDEF_MAX) { intmax_t subscript_diff = p_max - p_current;/* Handle error */ if ((p_max > p_current) && (subscript_diff > elem_count) ) } else { returnx = truetemp; } return false; } |
On the 64-bit scenario, this code correctly returns true.
...
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 |
|
439 S
440 S
586 S
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 |
...
http://www.eetimes.com/design/signal-processing-dsp/4007211/Standard-C-s-pointer-difference-typehttp://embedded-systems.com/columns/technicalinsights/202404371
Ptrdiff_t is evil
david_leblanc
2 Sep 2008 http://blogs.msdn.com/b/david_leblanc/archive/2008/09/02/ptrdiff-t-is-evil.aspx