Skip to content

Commit 6845cdc

Browse files
committed
RULE-7-0-4 - InappropriateBitwiseOrShiftOperands
Detects inappropriate operands for bitwise and shift operators that violate type requirements. Identifies signed operands in bitwise operations and improper shift operand types that may cause undefined behavior. [a]
1 parent 89485d5 commit 6845cdc

File tree

4 files changed

+271
-0
lines changed

4 files changed

+271
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* @id cpp/misra/inappropriate-bitwise-or-shift-operands
3+
* @name RULE-7-0-4: The operands of bitwise operators and shift operators shall be appropriate
4+
* @description Bitwise and shift operators should only be applied to operands of appropriate types
5+
* and values to avoid implementation-defined or undefined behavior.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-7-0-4
10+
* scope/single-translation-unit
11+
* external/misra/enforcement/decidable
12+
* external/misra/obligation/required
13+
*/
14+
15+
import cpp
16+
import codingstandards.cpp.misra
17+
import codingstandards.cpp.misra.BuiltInTypeRules
18+
19+
predicate isSignedType(NumericType t) { t.getSignedness() = Signed() }
20+
21+
predicate isUnsignedType(NumericType t) { t.getSignedness() = Unsigned() }
22+
23+
predicate isConstantExpression(Expr e) {
24+
e instanceof Literal or
25+
e.isConstant()
26+
}
27+
28+
predicate isValidShiftConstantRange(Expr right, Type leftType) {
29+
exists(int value |
30+
value = right.getValue().toInt() and
31+
value >= 0 and
32+
value < leftType.getSize() * 8
33+
)
34+
}
35+
36+
predicate isSignedConstantLeftShiftException(LShiftExpr shift) {
37+
exists(Expr left, Expr right, NumericType leftType, int leftVal, int rightVal, int maxBit |
38+
left = shift.getLeftOperand() and
39+
right = shift.getRightOperand() and
40+
leftType = left.getType() and
41+
isConstantExpression(left) and
42+
isConstantExpression(right) and
43+
isSignedType(leftType) and
44+
isValidShiftConstantRange(right, leftType) and
45+
leftVal = left.getValue().toInt() and
46+
rightVal = right.getValue().toInt() and
47+
leftVal >= 0 and
48+
maxBit = leftType.getSize() * 8 - 1 and
49+
// Check that no set bit is shifted into or beyond the sign bit
50+
leftVal * 2.pow(rightVal) < 2.pow(maxBit)
51+
)
52+
}
53+
54+
class BinaryShiftOperation extends BinaryOperation {
55+
BinaryShiftOperation() {
56+
this instanceof LShiftExpr or
57+
this instanceof RShiftExpr
58+
}
59+
}
60+
61+
class AssignShiftOperation extends AssignOperation {
62+
AssignShiftOperation() {
63+
this instanceof AssignLShiftExpr or
64+
this instanceof AssignRShiftExpr
65+
}
66+
}
67+
68+
from Expr x, string message
69+
where
70+
not isExcluded(x, ConversionsPackage::inappropriateBitwiseOrShiftOperandsQuery()) and
71+
(
72+
// Binary bitwise operators (excluding shift operations) - both operands must be unsigned
73+
exists(BinaryBitwiseOperation op |
74+
not op instanceof BinaryShiftOperation and
75+
op = x and
76+
not (
77+
isUnsignedType(op.getLeftOperand().getExplicitlyConverted().getType()) and
78+
isUnsignedType(op.getRightOperand().getExplicitlyConverted().getType())
79+
) and
80+
message =
81+
"Binary bitwise operator '" + op.getOperator() + "' requires both operands to be unsigned."
82+
)
83+
or
84+
// Compound assignment bitwise operators - both operands must be unsigned
85+
exists(AssignBitwiseOperation op |
86+
not op instanceof AssignShiftOperation and
87+
op = x and
88+
not (
89+
isUnsignedType(op.getLValue().getExplicitlyConverted().getType()) and
90+
isUnsignedType(op.getRValue().getExplicitlyConverted().getType())
91+
) and
92+
message =
93+
"Compound assignment bitwise operator '" + op.getOperator() +
94+
"' requires both operands to be unsigned."
95+
)
96+
or
97+
// Bit complement operator - operand must be unsigned
98+
exists(ComplementExpr comp |
99+
comp = x and
100+
not isUnsignedType(comp.getOperand().getExplicitlyConverted().getType()) and
101+
message = "Bit complement operator '~' requires unsigned operand."
102+
)
103+
or
104+
// Shift operators - left operand must be unsigned
105+
exists(BinaryShiftOperation shift |
106+
shift = x and
107+
not isUnsignedType(shift.getLeftOperand().getExplicitlyConverted().getType()) and
108+
not isSignedConstantLeftShiftException(shift) and
109+
message = "Shift operator '" + shift.getOperator() + "' requires unsigned left operand."
110+
)
111+
or
112+
// Shift operators - right operand must be unsigned or constant in valid range
113+
exists(BinaryShiftOperation shift, Expr right |
114+
shift = x and
115+
shift = x and
116+
right = shift.getRightOperand() and
117+
not isUnsignedType(right.getExplicitlyConverted().getType()) and
118+
not isValidShiftConstantRange(right, shift.getLeftOperand().getExplicitlyConverted().getType()) and
119+
message =
120+
"Shift operator '" + shift.getOperator() +
121+
"' requires unsigned right operand or constant in valid range."
122+
)
123+
)
124+
select x, message
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
| test.cpp:14:3:14:13 | ... & ... | Binary bitwise operator '&' requires both operands to be unsigned. |
2+
| test.cpp:15:3:15:13 | ... \| ... | Binary bitwise operator '\|' requires both operands to be unsigned. |
3+
| test.cpp:16:3:16:13 | ... ^ ... | Binary bitwise operator '^' requires both operands to be unsigned. |
4+
| test.cpp:18:3:18:13 | ... & ... | Binary bitwise operator '&' requires both operands to be unsigned. |
5+
| test.cpp:19:3:19:13 | ... \| ... | Binary bitwise operator '\|' requires both operands to be unsigned. |
6+
| test.cpp:30:3:30:15 | ... &= ... | Compound assignment bitwise operator '&=' requires both operands to be unsigned. |
7+
| test.cpp:31:3:31:15 | ... \|= ... | Compound assignment bitwise operator '\|=' requires both operands to be unsigned. |
8+
| test.cpp:32:3:32:15 | ... ^= ... | Compound assignment bitwise operator '^=' requires both operands to be unsigned. |
9+
| test.cpp:42:3:42:6 | ~ ... | Bit complement operator '~' requires unsigned operand. |
10+
| test.cpp:54:3:54:9 | ... << ... | Shift operator '<<' requires unsigned left operand. |
11+
| test.cpp:55:3:55:11 | ... << ... | Shift operator '<<' requires unsigned left operand. |
12+
| test.cpp:63:3:63:18 | ... << ... | Shift operator '<<' requires unsigned left operand. |
13+
| test.cpp:74:3:74:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. |
14+
| test.cpp:75:3:75:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. |
15+
| test.cpp:85:3:85:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. |
16+
| test.cpp:86:3:86:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. |
17+
| test.cpp:87:3:87:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. |
18+
| test.cpp:97:3:97:9 | ... << ... | Shift operator '<<' requires unsigned left operand. |
19+
| test.cpp:98:3:98:9 | ... << ... | Shift operator '<<' requires unsigned left operand. |
20+
| test.cpp:99:3:99:9 | ... << ... | Shift operator '<<' requires unsigned left operand. |
21+
| test.cpp:100:3:100:9 | ... << ... | Shift operator '<<' requires unsigned left operand. |
22+
| test.cpp:104:3:104:11 | ... << ... | Shift operator '<<' requires unsigned left operand. |
23+
| test.cpp:106:3:106:17 | ... << ... | Shift operator '<<' requires unsigned left operand. |
24+
| test.cpp:112:3:112:10 | ... << ... | Shift operator '<<' requires unsigned left operand. |
25+
| test.cpp:120:3:120:11 | ... >> ... | Shift operator '>>' requires unsigned left operand. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include <climits>
2+
#include <cstdint>
3+
4+
void test_binary_bitwise_operators_unsigned_operands() {
5+
std::uint32_t u32a = 0x12345678U;
6+
std::uint32_t u32b = 0x87654321U;
7+
std::int32_t s32a = 0x12345678;
8+
std::int32_t s32b = 0x87654321;
9+
10+
u32a & u32b; // COMPLIANT
11+
u32a | u32b; // COMPLIANT
12+
u32a ^ u32b; // COMPLIANT
13+
14+
s32a & s32b; // NON_COMPLIANT
15+
s32a | s32b; // NON_COMPLIANT
16+
s32a ^ s32b; // NON_COMPLIANT
17+
18+
s32a & u32b; // NON_COMPLIANT
19+
u32a | s32b; // NON_COMPLIANT
20+
}
21+
22+
void test_compound_assignment_bitwise_operators() {
23+
std::uint32_t u32 = 0x12345678U;
24+
std::int32_t s32 = 0x12345678;
25+
26+
u32 &= 0xFFFFU; // COMPLIANT
27+
u32 |= 0x1000U; // COMPLIANT
28+
u32 ^= 0x5555U; // COMPLIANT
29+
30+
s32 &= 0xFFFF; // NON_COMPLIANT
31+
s32 |= 0x1000; // NON_COMPLIANT
32+
s32 ^= 0x5555; // NON_COMPLIANT
33+
}
34+
35+
void test_bit_complement_operator() {
36+
std::uint32_t u32 = 0x12345678U;
37+
std::uint8_t u8 = 0x55U;
38+
std::int32_t s32 = 0x12345678;
39+
40+
~u32; // COMPLIANT
41+
~u8; // COMPLIANT
42+
~s32; // NON_COMPLIANT
43+
}
44+
45+
void test_shift_operators_left_operand_type() {
46+
std::uint32_t u32 = 0x12345678U;
47+
std::uint8_t u8 = 2U;
48+
std::int32_t s32 = 0x12345678;
49+
50+
1U << u8; // COMPLIANT
51+
1U << 31; // COMPLIANT
52+
u32 << 2U; // COMPLIANT
53+
54+
1 << u8; // NON_COMPLIANT
55+
s32 << 2U; // NON_COMPLIANT
56+
}
57+
58+
void test_shift_operators_with_promotion() {
59+
std::uint8_t u8 = 1U;
60+
std::uint16_t u16 = 2U;
61+
62+
static_cast<std::uint16_t>(u8 + u16) << 2U; // COMPLIANT
63+
(u8 + u16) << 2U; // NON_COMPLIANT
64+
}
65+
66+
void test_shift_operators_right_operand_unsigned() {
67+
std::uint32_t u32 = 0x12345678U;
68+
std::uint8_t u8 = 2U;
69+
std::int8_t s8 = 2;
70+
71+
u32 << u8; // COMPLIANT
72+
u32 >> u8; // COMPLIANT
73+
74+
u32 << s8; // NON_COMPLIANT
75+
u32 >> s8; // NON_COMPLIANT
76+
}
77+
78+
void test_shift_operators_right_operand_constant_range() {
79+
std::uint32_t u32 = 0x12345678U;
80+
81+
u32 << 0; // COMPLIANT
82+
u32 << 31; // COMPLIANT
83+
u32 >> 15; // COMPLIANT
84+
85+
u32 << 32; // NON_COMPLIANT
86+
u32 << 64; // NON_COMPLIANT
87+
u32 >> 32; // NON_COMPLIANT
88+
}
89+
90+
void test_exception_signed_constant_left_operand() {
91+
// Exception cases for signed constant expressions
92+
1 << 30; // COMPLIANT
93+
2 << 29; // COMPLIANT
94+
4 << 28; // COMPLIANT
95+
8 << 27; // COMPLIANT
96+
97+
1 << 31; // NON_COMPLIANT
98+
2 << 30; // NON_COMPLIANT
99+
4 << 29; // NON_COMPLIANT
100+
8 << 28; // NON_COMPLIANT
101+
102+
1LL << 31; // COMPLIANT - 64 bit type
103+
1LL << 62; // COMPLIANT - 64 bit type
104+
1LL << 63; // NON_COMPLIANT - 64 bit type
105+
106+
0x40000000 << 1; // NON_COMPLIANT
107+
}
108+
109+
void test_exception_non_constant_signed_operand() {
110+
std::int32_t s32 = 1;
111+
112+
s32 << 2; // NON_COMPLIANT
113+
}
114+
115+
void test_right_shift_signed_operands() {
116+
std::uint32_t u32 = 0x80000000U;
117+
std::int32_t s32 = -1;
118+
119+
u32 >> 1U; // COMPLIANT
120+
s32 >> 1U; // NON_COMPLIANT
121+
}

0 commit comments

Comments
 (0)