-
Notifications
You must be signed in to change notification settings - Fork 12k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid undefined behavior in shift operators during constant folding of DIExpressions. #116466
base: main
Are you sure you want to change the base?
Conversation
…f DIExpressions. Bit shift operations with a shift operand greater than or equal to the bit width of the (promoted) value type result in undefined behavior according to C++ [expr.shift]p1. This change adds checking for this situation and avoids attempts to constant fold DIExpressions that would otherwise provoke such behavior. An existing test that presumably intended to exercise shifts at the UB boundary has been updated; it now checks for shifts of 64 bits instead of 65. This issue was reported by a static analysis tool; no actual cases of shift operations that would result in undefined behavior in practice have been identified.
@llvm/pr-subscribers-llvm-ir Author: Tom Honermann (tahonermann) ChangesBit shift operations with a shift operand greater than or equal to the bit width of the (promoted) value type result in undefined behavior according to C++ [[expr.shift]p1](https://eel.is/c++draft/expr.shift#1). This change adds checking for this situation and avoids attempts to constant fold Full diff: https://github.com/llvm/llvm-project/pull/116466.diff 2 Files Affected:
diff --git a/llvm/lib/IR/DIExpressionOptimizer.cpp b/llvm/lib/IR/DIExpressionOptimizer.cpp
index 2bb8eac348c8e9..5bd18dcf049060 100644
--- a/llvm/lib/IR/DIExpressionOptimizer.cpp
+++ b/llvm/lib/IR/DIExpressionOptimizer.cpp
@@ -59,12 +59,14 @@ foldOperationIfPossible(uint64_t Const1, uint64_t Const2,
return Const1 - Const2;
}
case dwarf::DW_OP_shl: {
- if ((uint64_t)countl_zero(Const1) < Const2)
+ if (Const2 >= std::numeric_limits<uint64_t>::digits ||
+ (uint64_t)countl_zero(Const1) < Const2)
return std::nullopt;
return Const1 << Const2;
}
case dwarf::DW_OP_shr: {
- if ((uint64_t)countr_zero(Const1) < Const2)
+ if (Const2 >= std::numeric_limits<uint64_t>::digits ||
+ (uint64_t)countr_zero(Const1) < Const2)
return std::nullopt;
return Const1 >> Const2;
}
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index fbdab1975df725..628221339c89bf 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -3541,12 +3541,12 @@ TEST_F(DIExpressionTest, Fold) {
ResExpr = DIExpression::get(Context, ResOps);
EXPECT_EQ(E, ResExpr);
- // Test a left shift greater than 64.
+ // Test a left shift greater than 63.
Ops.clear();
Ops.push_back(dwarf::DW_OP_constu);
Ops.push_back(1);
Ops.push_back(dwarf::DW_OP_constu);
- Ops.push_back(65);
+ Ops.push_back(64);
Ops.push_back(dwarf::DW_OP_shl);
Expr = DIExpression::get(Context, Ops);
E = Expr->foldConstantMath();
@@ -3554,17 +3554,17 @@ TEST_F(DIExpressionTest, Fold) {
ResOps.push_back(dwarf::DW_OP_constu);
ResOps.push_back(1);
ResOps.push_back(dwarf::DW_OP_constu);
- ResOps.push_back(65);
+ ResOps.push_back(64);
ResOps.push_back(dwarf::DW_OP_shl);
ResExpr = DIExpression::get(Context, ResOps);
EXPECT_EQ(E, ResExpr);
- // Test a right shift greater than 64.
+ // Test a right shift greater than 63.
Ops.clear();
Ops.push_back(dwarf::DW_OP_constu);
Ops.push_back(1);
Ops.push_back(dwarf::DW_OP_constu);
- Ops.push_back(65);
+ Ops.push_back(64);
Ops.push_back(dwarf::DW_OP_shr);
Expr = DIExpression::get(Context, Ops);
E = Expr->foldConstantMath();
@@ -3572,7 +3572,7 @@ TEST_F(DIExpressionTest, Fold) {
ResOps.push_back(dwarf::DW_OP_constu);
ResOps.push_back(1);
ResOps.push_back(dwarf::DW_OP_constu);
- ResOps.push_back(65);
+ ResOps.push_back(64);
ResOps.push_back(dwarf::DW_OP_shr);
ResExpr = DIExpression::get(Context, ResOps);
EXPECT_EQ(E, ResExpr);
|
@@ -59,12 +59,14 @@ foldOperationIfPossible(uint64_t Const1, uint64_t Const2, | |||
return Const1 - Const2; | |||
} | |||
case dwarf::DW_OP_shl: { | |||
if ((uint64_t)countl_zero(Const1) < Const2) | |||
if (Const2 >= std::numeric_limits<uint64_t>::digits || | |||
(uint64_t)countl_zero(Const1) < Const2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drive-by nit: named casts are preferred, i.e., static_cast.
Bit shift operations with a shift operand greater than or equal to the bit width of the (promoted) value type result in undefined behavior according to C++ [expr.shift]p1. This change adds checking for this situation and avoids attempts to constant fold
DIExpressions
that would otherwise provoke such behavior. An existing test that presumably intended to exercise shifts at the UB boundary has been updated; it now checks for shifts of 64 bits instead of 65. This issue was reported by a static analysis tool; no actual cases of shift operations that would result in undefined behavior in practice have been identified.