Intro
Summarized reference from the book “The Art of Software Security Assessment – Identifying and Preventing Software Vulnerabilities” By Mark Dowd, John McDonald, Justin Schuh.
Please go read this chapter and book in its entirety. You won’t be disappointed.
Integer Promotions
Integer promotions specify how C takes a narrow integer data type, such as a char or short, and converts it to an int (or, in rare cases, to an unsigned int). This up-conversion, or promotion, is used for two different purposes:
- Certain operators in C require an integer operand of type int or unsigned int. For these operators, C uses the integer promotion rules to transform a narrower integer operand into the correct typeint or unsigned int.
- Integer promotions are a critical component of C’s rules for handling arithmetic expressions, which are called the usual arithmetic conversions.
For arithmetic expressions involving integers, integer promotions are usually applied to both operands.
There’s a useful concept from the C standards: Each integer data type is assigned what’s known as an integer conversion rank. These ranks order the integer data types by their width from lowest to highest. The signed and unsigned varieties of each type are assigned the same rank. The following abridged list sorts integer types by conversion rank from high to low. The C standard assigns ranks to other integer types, but this list should suffice for this discussion:
Integer conversion rank
long long int, unsigned long long int |
long int, unsigned long int |
unsigned int, int |
unsigned short, short |
char, unsigned char, signed char |
_Bool |
Integer Promotions Summary
If you apply the integer promotions to a variable, what happens? First, if the variable isn’t an integer type or a bit field, the promotions do nothing. Second, if the variable is an integer type, but its integer conversion rank is greater than or equal to that of an int, the promotions do nothing. Therefore, ints, unsigned ints, long ints, pointers, and floats don’t get altered by the integer promotions.
So, the integer promotions are responsible for taking a narrower integer type or bit field and promoting it to an int or unsigned int. This is done in a straightforward fashion: If a value-preserving transformation to an int can be performed, it’s done. Otherwise, a value-preserving conversion to an unsigned int is performed.
The basic rule of thumb is this: If an integer type is narrower than an int, integer promotions almost always convert it to an int. A few common types.
Results of Integer Promotions
Source Type | Result Type | Rationale |
unsigned char | int | Promote; source rank less than int rank |
char | int | Promote; source rank less than int rank |
short | int | Promote; source rank less than int rank |
unsigned short | int | Promote; source rank less than int rank |
unsigned int: 24 | int | Promote; bit field of unsigned int |
unsigned int: 32 | unsigned int | Promote; bit field of unsigned int |
int | int | Don’t promote; source rank equal to int rank |
unsigned int | unsigned int | Don’t promote; source rank equal to int rank |
long int | long int | Don’t promote; source rank greater than int rank |
float | float | Don’t promote; source not of integer type |
char * | char * | Don’t promote; source not of integer type |
Integer Promotion Applications
Unary + Operator
The unary + operator performs integer promotions on its operand. For example, if the bob variable is of type char, the resulting type of the expression (+bob) is int, whereas the resulting type of the expression (bob) is char.
Unary – Operator
The unary – operator does integer promotion on its operand and then does a negation. Regardless of whether the operand is signed after the promotion, a twos complement negation is performed, which involves inverting the bits and adding 1.
Unary ~ Operator
The unary ~ operator does a ones complement of its operand after doing an integer promotion of its operand. This effectively performs the same operation on both signed and unsigned operands for twos complement implementations: It inverts the bits.
Bitwise Shift Operators
The bitwise shift operators >> and << shift the bit patterns of variables. The integer promotions are applied to both arguments of these operators, and the type of the result is the same as the promoted type of the left operand, as shown in this example:
char a = 1; char c = 16; int bob; bob = a << c;
a is converted to an integer, and c is converted to an integer. The promoted type of the left operand is int, so the type of the result is an int. The integer representation of a is left-shifted 16 times.
Switch Statements
Integer promotions are used in switch statements. The general form of a switch statement is something like this:
switch (controlling expression) { case (constant integer expression): body; break; default: body; break; }
The integer promotions are used in the following way: First, they are applied to the controlling expression, so that expression has a promoted type. Then, all the integer constants are converted to the type of the promoted control expression.
Function Invocations
Older C programs using the K&R semantics don’t specify the data types of arguments in their function declarations. When a function is called without a prototype, the compiler has to do something called default argument promotions. Basically, integer promotions are applied to each function argument, and any arguments of the float type are converted to arguments of the double type. Consider the following example:
int jim(bob) char bob; { printf("bob=%d\n", bob); } int main(int argc, char **argv) { char a=5; jim(a); }
In this example, a copy of the value of a is passed to the jim() function. The char type is first run through the integer promotions and transformed into an integer. This integer is what’s passed to the jim() function. The code the compiler emits for the jim() function is expecting an integer argument, and it performs a direct conversion of that integer back into a char format for the bob variable.
Usual Arithmetic Conversions
In many situations, C is expected to take two operands of potentially divergent types and perform some arithmetic operation that involves both of them. The C standards spell out a general algorithm for reconciling two types into a compatible type for this purpose. This procedure is known as the usual arithmetic conversions. The goal of these conversions is to transform both operands into a common real type, which is used for the actual operation and then as the type of the result. These conversions apply only to the arithmetic types integer and floating point types. The following sections tackle the conversion rules.