CertC-INT17ΒΆ
Define integer constants in an implementation-independent manner
Required inputs: IR
Integer constants are often used as masks or specific bit values. Frequently, these constants are expressed in hexadecimal form to indicate to the programmer how the data might be represented in the machine. However, hexadecimal integer constants are frequently used in a nonportable manner.
Noncompliant Code Example
In this pedagogical noncompliant code example, the
flipbits() function complements the value stored in
x by performing a bitwise exclusive OR against a mask with all
bits set to 1. For
implementations
where
unsigned long is represented by a 32-bit value, each bit of
x is correctly complemented.
/* (Incorrect) Set all bits in mask to 1 */
const unsigned long mask = 0xFFFFFFFF;
unsigned long flipbits(unsigned long x) {
return x ^ mask;
}
However, on implementations where values of type
unsigned long are represented by greater than 32 bits,
mask will have leading 0s. For example, on implementations where
values of type
unsigned long are 64 bits long,
mask is assigned the value
0x00000000FFFFFFFF. Consequently, only the lower-order bits of
x are complemented.
Compliant Solution (-1)
In this compliant solution, the integer constant
-1 is used to set all bits in
mask to 1. The integer constant
-1 is of type
signed int. Because
-1 cannot be represented by a variable of type
unsigned long, it is converted to a representable number according
to the rule in subclause 6.3.1.3, paragraph 2, of the C Standard [
ISO/IEC
9899:2011]:
[If the value can't be represented by the new type and] if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
"One more than the maximum value that can be represented in the new type,"
ULONG_MAX + 1, is added to
-1, resulting in a right-side value of
ULONG_MAX. The representation of
ULONG_MAX is guaranteed to have all bits set to 1 by subclause
6.2.6.2, paragraph 1:
For unsigned integer types other than
unsigned char, the bits of the object representation shall be divided into two groups: value bits and padding bits (there need not be any of the latter). If there are N valuebits, each bit shall represent a different power of 2 between 1 and 2N - 1, so that objects of that type shall be capable of representing values from 0 to 2N - 1 using a pure binary representation; this shall be known as the value representation. The values of any padding bits are unspecified.
By the same reasoning,
-1 is suitable for setting all bits to one of any unsigned integer
variable. Subclause 6.2.6.1, paragraph 3, guarantees the same results for
unsigned char:
Values stored in unsigned bit-fields and objects of type
unsigned charshall be represented using a pure binary notation.
/* (Correct) Set all bits in mask to 1 */
const unsigned long mask = -1;
unsigned long flipbits(unsigned long x) {
return x ^ mask;
}
Noncompliant Code Example
In this noncompliant code example, a programmer attempts to set the most significant bit:
const unsigned long mask = 0x80000000; unsigned long x; /* Initialize x */ x |= mask;
This code has the desired effect for implementations where
unsigned long has a precision of 32 bits but not for
implementations where
unsigned long has a precision of 64 bits.
Compliant Solution
A portable (and safer) way of setting the high-order bit is to use a shift expression, as in this compliant solution:
const unsigned long mask = ~(ULONG_MAX >> 1); unsigned long x; /* Initialize x */ x |= mask;
Risk Assessment
Vulnerabilities are frequently introduced while porting code. A buffer overflow vulnerability may result, for example, if an incorrectly defined integer constant is used to determine the size of a buffer. It is always best to write portable code, especially when there is no performance overhead for doing so.
| Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| INT17-C | High | Probable | Low | P18 | L1 |
Related Guidelines
| SEI CERT C++ Coding Standard | VOID INT17-CPP. Define integer constants in an implementation-independent manner |
Bibliography
| [ Dewhurst 2002] | Gotcha #25, "#define Literals" |
| [ ISO/IEC 9899:2011] | Subclause 6.2.6, "Representations of Types" Subclause 6.3.1.3, "Signed and Unsigned Integers" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
non_portable_int |
This constant definition may be non-portable. |
None |
False |
Options
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
This rule has no individual options.