The range of valid enumeration values for an enumeration type is defined as follows in the C++ Working Draft, Section 7.2, paragraph 7:
For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type. Otherwise, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a twoâs complement representation and 0 for a oneâs complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin| ? K, |emax|) and equal to 2^M ? 1, where M is a non-negative integer. bmin is zero if emin is non-negative and ?(bmax + K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M, 1) if bmin is zero and M + 1 otherwise. It is possible to define an enumeration that has values not defined by any of its enumerators. If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0.
When converting an arithmetic or enumeration type to an enumeration type, the value is unchanged if it is in the range of enumeration values of the enumeration type. Otherwise, the value is unspecified.
To avoid unexpected behavior, the value being converted must be inside of the range of enumeration values. Furthermore, if it is necessary to check for out-of-range values dynamically, it must be done before the conversion.
Noncompliant Code Example (Bounds checking)
This noncompliant code example attempts to check for an out-of-bounds condition. However, it is doing so after the conversion, so the result of the conversion is unspecified and the statement may have no effect.
enum enum_type { E_A, E_B }; int int_var = -1; enum_type enum_var = static_cast<enum_type>(int_var); if (enum_var < E_A) { // handle error }
Compliant Solution (Bounds checking)
This compliant solution checks for an out-of-bounds condition before the conversion to guarantee there is no unspecified result.
enum enum_type { E_A, E_B }; int int_var = -1; if (int_var < E_A || int_var > E_B) { // handle error } enum_type enum_var = static_cast<enum_type>(int_var);
Noncompliant Code Example (Switch statement)
This noncompliant code may result in a truncation of the value of int_var
when converted to type enum_type
, resulting in execution of either case E_A
or E_B
instead of the default case.
enum enum_type { E_A, E_B }; int int_var = 5; switch (static_cast<enum_type>(int_var)) { case E_A: // some action A case E_B: // some action B default: // handle error }
Compliant Solution (Switch statement)
This compliant solution checks for an out-of-bounds condition before the conversion to guarantee that there are no unspecified values and, consequently, no truncation.
std::cout << "case A" << std::endl; enum enum_type { E_A, E_B }; int int_var = 5; if (int_var < E_A || int_var > E_B) { // handle error } switch (static_cast<enum_type>(int_var)) { case E_A: // some action A case E_B: // some action B default: // handle error }
Noncompliant Code Example (For loop)
This noncompliant code may result in an infinite loop instead of the expected behavior of looping through all enumeration values. The violation occurs at the end of the loop, when incrementing enum_var
from the last valid value E_G
produces an unspecified result.
enum enum_type { E_A = 1, E_B, E_C, E_D, E_E, E_F, E_G }; for(enum_type enum_var = E_A; enum_var <= E_G; enum_var = static_cast<enum_var>(enum_var+1)) { // some action }
Implementation Details
GCC 4.4.3 compiles this into an infinite loop.
Compliant Solution (For loop)
This compliant solution prevents any out-of-bounds arithmetic on the enumeration type.
enum enum_type { E_A = 1, E_B, E_C, E_D, E_E, E_F, E_G }; for(int i = E_A; i <= E_G; i = i+1) { // some action }
Risk Assessment
Unexpected behavior can lead to a buffer overflow and the execution of arbitrary code by an attacker. This is most likely if the program in one case checks the value correctly and then fails to do so later. Such a situation could allow an attacker to avoid verification of a buffer's length, and so on.
Automated detection should be possible for most cases, but it might not be able to guarantee if the value in range.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
INT36-CPP |
high |
probable |
high |
P6 |
L2 |
Bibliography
[Becker 09] Section 7.2, "Enumeration declarations"
INT35-CPP. Evaluate integer expressions in a larger size before comparing or assigning to that size 04. Integers (INT) 05. Floating Point Arithmetic (FLP)