Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Integer constants are often used as masks or specific bit values. Frequently, these constants are expressed in hexadecimal form to communicate indicate to the programmer how the data is might be represented in the machine. But this could lead to making assumptions about the system which might not hold true.

A hexadecimal constant without a suffix is defined as an unsigned int if it can be represented in 32 bits and if the high order bit is turned on. For example, the constant 0xFFFFFFFFL is a signed long on a 32-bit system (this sets all the bits to '1'). But on a 64-bit system, only the lower order 32-bits are set, resulting in the value 0x00000000FFFFFFFF. The following examples assume two's complement arithmetic.

Noncompliant Solution

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 complementedThe noncompliant solution attempts to take two's complement of a long using a mask. This solution works as intended when compiled and run on 32-bit machines, but its 64-bit equivalent binary will not run correctly.

Code Block
bgColor#FFCCCC
#define MASK   0xFFFFFFFFL
...
long x = -1L;

/*should not do bitwise operations on signed numbers*/
unsigned long u_x = (unsigned long) x;
unsigned long positive_u_x = (u_x ^ MASK) + 1;

long positive_x = (signed long) positive_u_x;

Compliant Solution

If you want to turn all the bits on, a portable (and safer) way to do this is to define a constant with a value of ~0. This turns all the bits on regardless of the arithmetic:

Code Block
bgColor#CCCCFF
#define MASK   ~0
...
long x = -1L;

/*should not do bitwise operations on signed numbers*/
unsigned long u_x = (unsigned long) x;
unsigned long positive_u_x = (u_x ^ MASK) + 1;

long positive_x = (signed long) positive_u_x;

Compliant Solution

Another compliant solution would be to use a constant, rather than a macro.

Code Block
bgColor#CCCCFF
long MASK = ~0;
...
long x = -1L;

/*should not do bitwise operations on signed numbers*/
unsigned long u_x = (unsigned long) x;
unsigned long positive_u_x = (u_x ^ MASK) + 1;

long positive_x = (signed long) positive_u_x;

Constants can be explicitly typed, avoiding the int/long ambiguity described above. For more information comparing constants and macros, see DCL06-C. Use meaningful symbolic constants to represent literal values in program logic.

Noncompliant Solution

Another problem that might arise is the setting of the most significant bit. On a 32-bit system, the constant 0x80000000 is used:

Code Block
bgColor#FFCCCC
#define MASK   0x80000000L
...
long x = -1L;

unsigned long u_x = (unsigned long) x;
unsigned long ones_complement_u_x = (u_x ^ MASK);

long ones_complement_x = (signed long) ones_complement_u_x;

Compliant Solution

A more portable way of doing this is to use a shift expression:

Code Block
bgColor#CCCCFF
#define MASK   (1L << ((sizeof(long) * CHAR_BIT) - 1))
...
long x = -1L;

unsigned long u_x = (unsigned long) x;
unsigned long ones_complement_u_x = (u_x ^ MASK);

long ones_complement_x = (signed long) ones_complement_u_x;

Refer to INT02-C. Understand integer conversion rules for details about how integer conversion can affect security in other ways.

Risk Assessment

When porting code, there are high chances of new insecurities getting introduced. In the case of numeric constants used to define the size of input strings, there are chances that some buffer checks might get invalidated, which could create the potential for buffer overflow style attacks. If you know that you will have to port your code in the future to other data models, writing portable code (without making any assumptions about the data model) in the beginning is recommended.

langc
/* (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 char shall be represented using a pure binary notation.

Code Block
bgColor#CCCCFF
langc
/* (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:

Code Block
bgColor#FFCCCC
langc
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:

Code Block
bgColor#CCCCFF
langc
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

Detectable

Repairable

Priority

Level

INT17-C

High

Probable

No

No

P6

L3

Automated Detection

Tool

Version

Checker

Description

Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-INT17
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C1245
C1246
C1247

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

PRE12-C

high

probable

low

P6

L2


Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Other Languages

...

Related Guidelines

...

...

...

...

...

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"


...

Image Added Image Added

References

Wiki Markup
\[[Dewhurst 02|AA. C++ References#Dewhurst 02]\] Gotcha #25, "#define Literals"

PRE11-C. Do not conclude a single statement macro definition with a semicolon      01. Preprocessor (PRE)      Image Modified