Int: A Young Love
Early C has an innocent air. Take for example this bounds-checking function, which converts a file descriptor to a pointer, after checking that the file descriptor is a valid index:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Want a value in a register? Use
It does what it says on the tin.
(At least it did then.)
Types? Those are a syntactic convenience.
Go with the flow and use the native word size:
the default type is
and that’s nearly all you’ll need.
It’s the (default, so not explicitly declared)
type of both
rf in this function.
And what do you think the function’s return type is, eh?
The Love That Wouldn’t Die
Modern C shows its continuing love for
int in subtle ways
that will one day corrupt your code.
It’s subtle, because most of the time, C arithmetic just works, to the point where you can remain unaware of what’s actually going on when you perform some innocent-looking arithmetic.
(0xFFFF << 24)? Let’s see:
1 2 3 4
Well, that ain’t right.
And if you’ve got warnings turned on, your compiler might even be so kind as to warn you that you’re doing something boneheaded:
1 2 3 4 5
Int, what int?
I ordered a
uint64_t, thank you muchly.
But “‘int’ only has 32 bits”. And the compiler ran out of bits. And then, once it got done shifting the int around, it widened it to take up a full 64 bits, and the sign bit came with it:
But our int only had room for
Which widens to the
Integer Promotions and Arithmetic Conversions
So you see,
int is still very much the preferred type
for integral literals and arithmetic.
This preference is embedded in the core rules underlying C arithmetic:
- The integer promotions,
which describe how integral types smaller than
intget promoted to
- The usual arithmetic conversions, which describe how to pick a common type for the arguments to an arithmetic operation, and what the final result type should be.
Both rules make extensive use of the integer conversion rank of the various integral types, which we can roughly summarize as:
- Bigger integral types have a higher rank.
- Unsigned and signed types of the same size have the same rank.
Put all this together, and you can draw up a big spreadsheet of what value converts how with what other type of value, and how the arithmetic operation’s result type gets picked.
And that’s not even to speak of the fun we can have with the limited precision
provided by a fixed-width integral type,
namely overflow (
INT_MAX + 1) and underflow (
INT_MIN - 1).
What do you do?
- Crank up compiler warnings (
-Weverything -Wextra -Werror)
- Be suspicious of arithmetic:
- Are the types right, even after promotion?
- Can this overflow?
- Can this underflow?
- Be doubly suspicious of external data, whether from files, the network, or even your own web service.
Even CPU simulator writers can get it wrong, as shown by The cltq story.
Arithmetic problems regularly feature as security vulnerabilities; you can dig into this angle starting with INT02-C. Understand integer conversion rules from the CERT Secure Coding Standards.
And you can dive deep into the details, and several example vulnerabilities, starting with Type Conversions from the “C Language Issues for Application Security” chapter of McDonald et. al.’s Art of Software Security Assessment.