
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 non-portable manner.
Noncompliant Code Example
In this noncompliant code example, on implementations where sizeof(unsigned long) == sizeof(unsigned int)
, each bit of x
is correctly flipped using mask
.
However, on implementations where sizeof(unsigned long) > sizeof(unsigned int)
, mask
will have leading zeros, e.g. mask == 0x00000000FFFFFFFF
. Then, only the lower-order bits of x
will be flipped.
// (Incorrect) Set all bits in mask to one. const unsigned long mask = 0xFFFFFFFF; unsigned long flipbits(unsigned long x) { return x ^ mask; }
Compliant Solution (-1
)
In this compliant solution, the integer constant -1
cannot be represented by a variable of type unsigned long
. According to the C99 standard, Section 6.3.1.3, Paragraph 2,
[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-hand-side value of ULONG_MAX
. The representation of ULONG_MAX
is guaranteed to have all bits set to one by Section 6.2.6.1, Paragraph 3,
Values stored in unsigned bit-fields and objects of type
unsigned char
shall be represented using a pure binary notation.
(emphasis added)
and by Section 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 value bits, 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.
(emphasis added)
By the same reasoning, -1
is suitable for setting all bits to one of any unsigned integer variable.
// (Correct) Set all bits in mask to one. const unsigned long mask = -1; unsigned long flipbits(unsigned long x) { return x ^ mask; }
Noncompliant Code Example
In this noncompliant code example, a programmer is attempting to set the most significant bit.
const unsigned long mask = 0x80000000; unsigned long x; /* Initialize x */ x = (x ^ mask) + 1;
This code has the desired effect for implementations where unsigned long
have a precision of 32 bits but not on 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 = (1 << ((sizeof(unsigned long) * CHAR_BIT) - 1)) unsigned long x; /* Initialize x */ x = (x ^ mask) + 1;
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 Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C++ Secure Coding Standard: INT17-CPP. Define integer constants in an implementation-independent manner
ISO/IEC 9899-1999 Section 6.4.4.1, "Integer constants"
Bibliography
[Dewhurst 2002] Gotcha #25, "#define Literals"