Using enumerations
In C and C++, enumerated types, also called enumerations, can greatly increase the readability and maintainability of source code. However, using enumerations improperly can cause security issues.
Best practices
You can use the following guidelines to help reduce the risk of security issues when using enumerated types:
- Enumerated types in C and C++ might be signed or unsigned depending on the compiler.
- Any integer value is valid, not just those defined by a symbol.
- Check the compiler documentation for command-line switches that might change the way enumerated types are handled.
- Check that a value is in a legal range and ensure that there is both an upper and lower bounds check.
- When using switch statements, make sure that there is a default clause.
- When using switch statements, make sure that variables set by some cases are initialized before the switch.
- Use enumerated types for a single contiguous range of values.
- When using multiple discrete values, consider using #define rather than enum.
Enumerated types
Enumerated types assign constant integer values to symbols. According to the C standard, the expression that defines the value of an enumeration constant shall be an integer constant expression that has a value representable as an int. However, the implementation is compiler-dependent, so the same source compiled with two different compilers could behave differently. The QNX QCC and GCC compilers use unsigned values by default. If you are developing apps for multiple platforms and using other compilers, you should check the compiler documentation for implementation-specific information and keep in mind the unsigned nature of default enumerated values.
If a compiler uses unsigned integers by default, specifying a negative value will cause the compiler to use signed integers. Conversely, if a compiler uses signed integers, it's not possible to create an unsigned enumeration by, for example, setting one element of the enumeration to a value (such as 0xffffffff) that's too large for a signed integer.
To demonstrate this, consider the following example:
#include <stdio.h>
int main(void) {
enum Test1 { a, b, c };
enum Test2 { x = 1, y = 65536, z = 0xffffffff };
printf("a = %d, b = %d, c = %d\n", a, b, c);
if (z < 0)
printf("Signed: x = %d, y = %d, z = %d\n", x, y, z);
else
printf("Unsigned: x = %u, y = %u, z = %u\n", x, y, z);
return 0;
}
When this code is compiled with the GCC and run, the following output is produced:
a = 0, b = 1, c = 2 Signed: x = 1, y = 65536, z = 4294967295
Because enumerated types are integers, the guidance provided in Using integers applies. While arithmetic isn't particularly common with enumerated types, arithmetic is legal code and is often used in bounds checking.