Skip to content

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.

Updated 02 Nov 2026Checked against cli@de9b52a
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 - 1 shifts 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

  1. Widen before shifting: 1ULL << n, (uint64_t)v >> 32.
  2. When the amount is computed, check 0 <= n && n < width first.
  3. Build with -fsanitize=shift in tests to catch the cases a static rule cannot see.

References

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.

Edit this pageSources: cli/internal/sast/rules/vnx-1335.rego, cli/internal/sast/docs/vnx-1335.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.