VNX-1335: Incorrect Bitwise Shift of Integer
Reports C/C++ shifts by a constant at or past the operand's width, or by a negative constant, which are undefined behaviour: 1 << 40, 1u << 32, 1ULL << 64, (uint32_t)v >> 32, a uint32_t/int variable shifted by 32, x << -1. Suffixed or widened operands within their width are not reported.
| Field | Value |
|---|---|
| Rule ID | VNX-1335 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | C, C++ |
| CWE | CWE-1335 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | incorrect-bitwise-shift undefined-behavior integer cwe-1335 |
| Source | internal/sast/rules/vnx-1335.rego |
Overview
VNX-1335 maps to CWE-1335: Incorrect Bitwise Shift of Integer. In C and C++, shifting by a negative amount or by an amount greater than or equal to the width of the (promoted) left operand is undefined behaviour. An unsuffixed integer literal is an int, 32 bits on every mainstream platform, so 1 << 40 is undefined even when the result is assigned to a uint64_t.
Why This Matters
Compilers are free to assume undefined behaviour never happens. In practice x86 masks the shift count, so 1 << 32 often yields 1, and optimisers may fold the expression to anything. Masks and bit flags computed this way silently select the wrong bits; length or bounds calculations built on them can become memory-safety bugs.
What Gets Flagged
uint64_t mask = 1 << 40; // FLAGGED: int literal, width 32
mask |= (1u << 32); // FLAGGED: unsigned int, width 32
return (1ULL << 64) - 1; // FLAGGED: 64-bit literal shifted by 64
uint32_t top = (uint32_t)v >> 32; // FLAGGED: cast to 32 bits, then shifted by 32
uint32_t h = seed; carry = h >> 32; // FLAGGED: 32-bit variable
return v << -1; // FLAGGED: negative amount
What Is Not Flagged
- Widened operands within their width:
1ULL << 40,UINT64_C(1) << 40,(uint64_t)1 << 63,((uint64_t)hi << 32). - Variables whose nearest declaration above is 64-bit (
uint64_t crc,long long,size_t). - An amount that is part of a larger expression (
1 << 32 - 1shifts by 31), and shifts by a variable amount. - Comments, strings and macro parameters (
#define BIT(n) (1ULL << (n))). - A variable’s type is known only from a declaration within 60 lines above the shift.
Remediation
- Widen before shifting:
1ULL << n,(uint64_t)v >> 32. - When the amount is computed, check
0 <= n && n < widthfirst. - Build with
-fsanitize=shiftin tests to catch the cases a static rule cannot see.
References
- CWE-1335: Incorrect Bitwise Shift of Integer
- SEI CERT INT34-C: Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-1335
// nosec vnx-1335
Or record an ignore rule: vulnetix ignore add --rule vnx-1335 --reason "...". See Suppressing findings.