A data model defines the sizes assigned to standard data types. These data models are typically named using a XXXn pattern where X referes to a C type and n refers to a size (typically 32 or 64). ILP64, for example, means that int, long and pointer types are 64 bits wide, LP32 means that long and pointer are 32 bits wide, and LLP64 means that long long and pointer are 64 bits wide.
Common data models
Data Type |
LP32 |
ILP32 |
ILP64 |
LLP64 |
LP64 |
|---|---|---|---|---|---|
char |
8 |
8 |
8 |
8 |
8 |
short |
16 |
16 |
16 |
16 |
16 |
int |
16 |
32 |
64 |
32 |
32 |
long |
32 |
32 |
64 |
32 |
64 |
long long |
|
|
|
64 |
|
pointer |
32 |
32 |
64 |
64 |
64 |
The following observations are derived from the Development Tutorial by Marco van de Voort [[van de Voort 07]]:
- The usual programming model for current (Intel family) PC processors is ILP32.
- One issue with
longin C was that there are both code bases that expect pointer andlongto have the same size, while there are also large code bases that expectintand long to be the same size. The compatibility model LLP64 was designed to preservelongandintcompatibility by introducing a new type to remain compatible with pointer (long long). - LLP64 is the only data model that defines a size for the
long longtype. - LP32 is used as model for the win-16 APIs of Windows 3.1.
- Most Unix versions use LP64, primarily to conserve memory space compared to ILP64, including: 64-bit Linux, FreeBSD, NetBSD, and OpenBSD.
- Win64 uses the LLP64 model (also known as P64). This model conserves type compatibility between
longandint, but loses type compatibility betweenlongand pointer types. Any cast between a pointer and an existing type requires modification. - ILP64 is the easiest model to work with, because it retains compatibility with the ubiquitous ILP32 model, except specific assumptions that the core types are 32-bit. However this model requires significant memory, and both code and data size significantly increase.
<limits.h>
Possibly more important than knowing the number of bits for a given type, one can use macros defined in <limits.h> to determine the integral ranges of the standard integer types. For example, UINT_MAX is the largest possible value of an unsigned int, and LONG_MIN is the smallest possible value of a long int.
<stdint.h>
The <stdint.h> header introduces types with specific size restrictions that can be used to avoid dependence on a particular data model. For example, int_least32_t is the smallest signed integer type the implementation supports that contains at least 32 bits. The type uint_fast16_t is the fastest unsigned integer type the implementation supports that contains at least 16 bits. The type intmax_t is the largest signed integer type supported by the implementation. The following types are required to be available on all implementations.
Smallest Types |
signed |
unsigned |
|---|---|---|
8 bits |
int_least8_t |
uint_least8_t |
16 bits |
int_least16_t |
uint_least16_t |
32 bits |
int_least32_t |
uint_least32_t |
64 bits |
int_least64_t |
uint_least64_t |
Fastest Types |
signed |
unsigned |
8 bits |
int_fast8_t |
uint_fast8_t |
16 bits |
int_fast16_t |
uint_fast16_t |
32 bits |
int_fast32_t |
uint_fast32_t |
64 bits |
int_fast64_t |
uint_fast64_t |
Largest Types |
signed |
unsigned |
maximum |
intmax_t |
uintmax_t |
Additional types may be supported by an implementation, such as int8_t, a type of exactly 8 bits, and uintptr_t, a type large enough to hold a converted void *, if such an integer exists in the implementation.
Non-Compliant Code Example
The following non-compliant code attempts to guarantee that all bits of a multiplication of two unsigned values will be retained by performing arithmetic in the type unsigned long. This works for the LP32 and LP64 models, but not for others.
unsigned a, b; unsigned long c; /* ... */ c = (unsigned long)a * b; /* not guaranteed to fit */
Compliant Solution
This compliant solution uses the largest unsigned integer type available, if it is guaranteed to hold the result. If it is not, another solution must be found, as discussed in INT32-C. Ensure that integer operations do not result in an overflow.
#if UINTMAX_MAX < (UINT_MAX*UINT_MAX) #error No safe type is available. #endif /* ... */ unsigned a, b; uintmax_t c; /* ... */ c = (uintmax_t)a * b; /* guaranteed to fit, verified above */
Risk Assessment
Understanding the data model used by your implementation is necessary to avoid making errors about the range of values that can be represented using integer types.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
|---|---|---|---|---|---|
INT00-A |
1 (low) |
1 (unlikely) |
1 (high) |
P1 |
L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[van de Voort 07]]
[[Open Group 97]]
04. Integers (INT) 04. Integers (INT) INT01-A. Use rsize_t or size_t for all integer values representing the size of an object