Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: REM Cost Reform

Integer types in C have two metrics associated with them, a both a size and a width precision. The size indicates the number of bytes used by a variable, an object and can be retrieved for any variable object or type using the sizeof operator, while the width indicates the .  The precision of an integer type is the number of bits that are actually used to encode the integer's value. The C standard permits an integer to contain internal padding bits; these it uses to represent values, excluding any sign and padding bits.

Padding bits contribute to the integer's size, but not to its widthprecision. Consequently, inferring the precision of an integer type may have a size that could be used to incorrectly assume its width is larger than it actually is.  Do from its size may result in too large a value, which can then lead to incorrect assumptions about the numeric range of these types.  Programmers should use correct integer precisions in their code, and in particular, should not use the sizeof operator to compute the width precision of an integer type on architectures that use padding bits or in strictly conforming (that is, portable) programs.

Noncompliant Code Example

This noncompliant code example illustrates a function that produces 2 raised to the power of the function argument. To prevent undefined behavior , (See undefined behavior 48.) in compliance with INT34-C. Do not shift an expression by a negative number of bits or more bits than or by greater than or equal to the number of bits that exist in the operand, the function ensures that the argument is less than the number of bits used to store an a value of type unsigned int.

 

Code Block
bgColor#ffcccc
lang
languagec
#include <limits.h>
 
unsigned int pow2(unsigned int exp) {
  if (exp >= sizeof(
unsigned int) * CHAR_BIT) {
    /* 
handle
Handle error */
  }
  return 1 << exp;
}

However, if this code runs on a platform where unsigned int has one or more padding bits, it can still accept result in values for exp that are too large. For instanceexample, on a platform that stores unsigned int in 64 bits, but uses only 48 width bits could perform bits to represent the value, a left shift on an illegal value of 56 bits would result in undefined behavior (See undefined behavior 48.).

Compliant Solution (popcount())

This compliant solution eliminates the possibility of undefined behavior resulting from a left-shift operation on unsigned integers:

 

unsigned int ui_a;
unsigned int ui_b;
unsigned int uresult;
 
void func(void) {
  /* Initialize ui_a and ui_b */
  size_t width = /* width of unsigned int */
  if (ui_b >= width) {
    /* Handle error condition */
  } else {
    uresult = ui_a << ui_b;
  }
 
  /* ... */
}

 

Modulo behavior resulting from left-shifting an unsigned integer type is permitted by this standard.

The computation of the correct width of any integer type can be nontrivial. On machines with no padding bits, the width can be computed directly from the integer's size:

 

size_t width = sizeof(unsigned int) * CHAR_BIT;

 

Some platforms provide a population count operation, which counts the number of bits that are set. This can be used to compute the width:

 

size_t width = _popcount(UINT_MAX);

 

For other platforms, you can implement _popcount yourself:

 

unsigned int val = UINT_MAX;
size_t width = 0;
while (val != 0) {
  width++;
  val >>= 1;
}

 

Noncompliant Code Example (Right Shift)

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2. If E1 has a signed type and a negative value, the resulting value isimplementation-defined and can be either an arithmetic (signed) shift:

Image Removed

or a logical (unsigned) shift:

Image Removed

This noncompliant code example fails to test whether the right operand is greater than or equal to the width of the promoted left operand, allowing undefined behavior:

 

unsigned int ui_a;
unsigned int ui_b;
unsigned int uresult;
 
void func(void) {
  /* Initialize ui_a and ui_b */
  uresult = ui_a >> ui_b;
 
  /* ... */
}

 

Making assumptions about whether a right shift is implemented as an arithmetic (signed) shift or a logical (unsigned) shift can also lead tovulnerabilities. See INT13-C. Use bitwise operators only on unsigned operands.

Compliant Solution (Right Shift)

This compliant solution tests the suspect shift operations to guarantee there is no possibility of undefined behavior:

 

#include <limits.h>
 
unsigned int ui_a;
unsigned int ui_b;
unsigned int uresult;
 
void func(void) {
  /* Initialize ui_a and ui_b */
  size_t width = /* width of unsigned int */
  if (ui_b >= width) {
    /* Handle error condition */
  } else {
    uresult = ui_a >> ui_b;
  }
  /* ... */
}

 

Implementation Details

GCC has no options to handle shifts by negative amounts or by amounts outside the width of the type predictably or trap on them; they are always treated as undefined. Processors may reduce the shift amount modulo the width of the type. For example, 32-bit shifts are implemented using the following instructions on IA-32:

 

sa[rl]l   %cl, %eax

 

The sa[rl]l instructions take a bit mask of the least significant 5 bits from %cl to produce a value in the range [0, 31] and then shift %eax that many bits:

 

64 bit shifts become
sh[rl]dl  %eax, %edx
sa[rl]l   %cl, %eax

 

where %eax stores the least significant bits in the doubleword to be shifted, and %edx stores the most significant bits.

Risk Assessment

Although shifting a negative number of bits or more bits than exist in the operand is undefined behavior in C, the risk is generally low because processors frequently reduce the shift amount modulo the width of the type.

uses a popcount() function, which counts the number of bits set on any unsigned integer, allowing this code to determine the precision of any integer type, signed or unsigned.

Code Block
bgColor#ccccff
langc
#include <stddef.h>
#include <stdint.h>
 
/* Returns the number of set bits */
size_t popcount(uintmax_t num) {
  size_t precision = 0;
  while (num != 0) {
    if (num % 2 == 1) {
      precision++;
    }
    num >>= 1;
  }
  return precision;
}
#define PRECISION(umax_value) popcount(umax_value) 

Implementations can replace the PRECISION() macro with a type-generic macro that returns an integer constant expression that is the precision of the specified type for that implementation. This return value can then be used anywhere an integer constant expression can be used, such as in a static assertion. (See DCL03-C. Use a static assertion to test the value of a constant expression.) The following type generic macro, for example, might be used for a specific implementation targeting the IA-32 architecture:

Code Block
bgColor#ccccff
langc
#define PRECISION(value)  _Generic(value, \
  unsigned char : 8, \
  unsigned short: 16, \
  unsigned int : 32, \
  unsigned long : 32, \
  unsigned long long : 64, \
  signed char : 7, \
  signed short : 15, \
  signed int : 31, \
  signed long : 31, \
  signed long long : 63)

The revised version of the pow2() function uses the PRECISION() macro to determine the precision of the unsigned type:

Code Block
bgColor#ccccff
langc
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
extern size_t popcount(uintmax_t);
#define PRECISION(umax_value) popcount(umax_value)  
unsigned int pow2(unsigned int exp) {
  if (exp >= PRECISION(UINT_MAX)) {
    /* Handle error */
  }
  return 1 << exp;
}

Implementation Details

Some platforms, such as the Cray Linux Environment (CLE; supported on Cray XT CNL compute nodes), provide a _popcnt instruction that can substitute for the popcount() function.

Code Block
bgColor#ccccff
langc
#define PRECISION(umax_value) _popcnt(umax_value)

Compliant Solution (C23)

The C23 standard provides various *_WIDTH macros that define the number of width bits for each integer type. This is effectively the size of the type (multiplied by 8) less the number of padding bits. The following compliant solution uses the UINT_WIDTH  type to obtain the width of an un

Code Block
bgColor#ccccff
langc
#include <limits.h>

unsigned int pow2(unsigned int exp) {
  if (exp >= UINT_WIDTH) {
    /* Handle error */
  }
  return 1 << exp;
}

Risk Assessment

Mistaking an integer's size for its precision can permit invalid precision arguments to operations such as bitwise shifts, resulting in undefined behavior.


Rule

Severity

Likelihood

Detectable

Repairable

Priority

Level

INT35-C

Low

Unlikely

No

No

P1

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

INT34-C

low

unlikely

medium

P2

L3

Automated Detection

ToolVersionCheckerDescription

Compass/ROSE

 

 

Can detect violations of this rule. Unsigned operands are detected when checking for INT13-C. Use bitwise operators only on unsigned operands

ECLAIR1.2CC2.INT34Partially implemented

Fortify SCA

5.0

 

Can detect violations of this rule with CERT C Rule Pack

LDRA tool suite

8.5.4

403 S

Partially implemented

PRQA QA-C8.1

0499
0500
0501
2790
2791 (D)
2792 (A)
2793 (S)

Partially implemented

Related Vulnerabilities

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

Related Guidelines

CERT C Secure Coding StandardINT13-C. Use bitwise operators only on unsigned operands
INT32-C. Ensure that operations on signed integers do not result in overflowCERT C++ Secure Coding StandardINT34-CPP. Do not shift a negative number of bits or more bits than exist in the operand
Astrée
Include Page
Astrée_V
Astrée_V

Supported: Astrée reports overflows due to insufficient precision.
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.ARITH.BIGSHIFT

Shift Amount Exceeds Bit Width

Cppcheck Premium

Include Page
Cppcheck Premium_V
Cppcheck Premium_V

premium-cert-int35-c
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C0582

C++3115


Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-INT35-a

Use correct integer precisions when checking the right hand operand of the shift operator

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule INT35-CChecks for situations when integer precisions are exceeded (rule fully covered)

 

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CWE 2.11CWE-681, Incorrect Conversion between Numeric Types

2017-10-30:MITRE:Unspecified Relationship

2018-10-18:CERT:Partial Overlap

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-190 and INT35-C

Intersection( INT35-C, CWE-190) = Ø

INT35-C used to map to CWE-190 but has been replaced with a new rule that has no overlap with CWE-190.

CWE-681 and INT35-C

Intersection(INT35-C, CWE-681) = due to incorrect use of integer precision, conversion from one data type to another causing data to be omitted or translated in a way that produces unexpected values

CWE-681INT35-C = list2, where list2 =

  • conversion from one data type to another causing data to be omitted or translated in a way that produces unexpected values, not involving incorrect use of integer precision

INT35-C - CWE-681= list1, where list1 = 

  • incorrect use of integer precision not related to conversion from one data type to another
ISO/IEC TR 24772:2013Arithmetic Wrap-around Error [FIF]

Bibliography

[Dowd 2006]Chapter 6, "C Language Issues"
[C99 Rationale 2003]
Subclause
6.5.7, "Bitwise Shift Operators"
[Seacord 2013]Chapter 5, "Integer Security"[Viega 2005]Section 5.2.7, "Integer Overflow"

A test program for this rule is available at www.securecoding.cert.org.

 



Image Removed Image Removed Image RemovedImage Added  Image Added   Image Added