-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Add needs_substs check to check_binary_op
.
#71386
Changes from all commits
32ff9a0
c61c17c
cf3b744
2d25b2b
2e6fab9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -521,72 +521,82 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | |||||
None | ||||||
} | ||||||
|
||||||
/// Check for overflow for a unary op. Like check_binary_op, safe to call even if the operand | ||||||
/// needs substs. In that case, no work is done. | ||||||
fn check_unary_op( | ||||||
&mut self, | ||||||
op: UnOp, | ||||||
arg: &Operand<'tcx>, | ||||||
source_info: SourceInfo, | ||||||
) -> Option<()> { | ||||||
if self.use_ecx(|this| { | ||||||
let val = this.ecx.read_immediate(this.ecx.eval_operand(arg, None)?)?; | ||||||
let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, val)?; | ||||||
Ok(overflow) | ||||||
})? { | ||||||
// `AssertKind` only has an `OverflowNeg` variant, so make sure that is | ||||||
// appropriate to use. | ||||||
assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); | ||||||
self.report_assert_as_lint( | ||||||
lint::builtin::ARITHMETIC_OVERFLOW, | ||||||
source_info, | ||||||
"this arithmetic operation will overflow", | ||||||
AssertKind::OverflowNeg, | ||||||
)?; | ||||||
if !arg.needs_subst() { | ||||||
if self.use_ecx(|this| { | ||||||
let val = this.ecx.read_immediate(this.ecx.eval_operand(arg, None)?)?; | ||||||
let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, val)?; | ||||||
Ok(overflow) | ||||||
})? { | ||||||
// `AssertKind` only has an `OverflowNeg` variant, so make sure that is | ||||||
// appropriate to use. | ||||||
assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); | ||||||
self.report_assert_as_lint( | ||||||
lint::builtin::ARITHMETIC_OVERFLOW, | ||||||
source_info, | ||||||
"this arithmetic operation will overflow", | ||||||
AssertKind::OverflowNeg, | ||||||
)?; | ||||||
} | ||||||
} | ||||||
|
||||||
Some(()) | ||||||
} | ||||||
|
||||||
/// Check a binary operand for overflow. Safe to call with values that need substs -- those | ||||||
/// values just won't be checked. | ||||||
fn check_binary_op( | ||||||
&mut self, | ||||||
op: BinOp, | ||||||
left: &Operand<'tcx>, | ||||||
right: &Operand<'tcx>, | ||||||
source_info: SourceInfo, | ||||||
) -> Option<()> { | ||||||
let r = | ||||||
self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?))?; | ||||||
// Check for exceeding shifts *even if* we cannot evaluate the LHS. | ||||||
if op == BinOp::Shr || op == BinOp::Shl { | ||||||
// We need the type of the LHS. We cannot use `place_layout` as that is the type | ||||||
// of the result, which for checked binops is not the same! | ||||||
let left_ty = left.ty(&self.local_decls, self.tcx); | ||||||
let left_size_bits = self.ecx.layout_of(left_ty).ok()?.size.bits(); | ||||||
let right_size = r.layout.size; | ||||||
let r_bits = r.to_scalar().ok(); | ||||||
// This is basically `force_bits`. | ||||||
let r_bits = r_bits.and_then(|r| r.to_bits_or_ptr(right_size, &self.tcx).ok()); | ||||||
if r_bits.map_or(false, |b| b >= left_size_bits as u128) { | ||||||
self.report_assert_as_lint( | ||||||
lint::builtin::ARITHMETIC_OVERFLOW, | ||||||
source_info, | ||||||
"this arithmetic operation will overflow", | ||||||
AssertKind::Overflow(op), | ||||||
)?; | ||||||
if !right.needs_subst() { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems entirely ad-hoc, and also not necessarily complete. What about And what is even happening that we see uninitialized data, and that no assertion anywhere fires to detect that we run the engine on code it cannot run? This is the most concerning part to me. Cc @oli-obk There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Whoops, you're right. I've left left out.
The only reason I felt comfortable with doing this is And what is even happening that we see uninitialized data, and that no assertion anywhere fires to detect that we run the engine on code it cannot run? Per #71353 (comment), isn't it just the error message raised during validation that's suspicious? I'm not sure that there's really uninitialised data here. I'll chuck a debugger on and see where the Err is being raised. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That was my first theory. But in #71353 (comment) it doesn't look like that any more.
If you guard There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I see where you are going... we can do the check for However, we still should figure out what is happening that we see uninitialized data coming back here. That is not a good failure mode. Instead we should have gotten an ICE somewhere saying "there are still substs here, this const cannot be evaluated". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm entirely confused by this issue. I don't like the fix in this PR, it doesn't address the issue, it just makes sure we don't run into it. It should be perfectly possible to evaluate After checking what causes "uninitialized raw ptr", I believe the fault is at rust/src/librustc_mir/interpret/place.rs Line 307 in c6b55ee
well, not really, this is where I'd expect the
We should probably not do that for any errors of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nono, that would be very wrong. We often invoke const eval on too generic code with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, we should just forward it for now, then. So, action items:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jumbatm do you want to work on this alternative solution? (It's okay if you don't, in that case we'll look for someone else to do it. :) ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm happy to keep going and work on the alternative solution. It definitely seems like the more robust way to fix the issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome, thanks. :) Probably best to make that a new PR, as most comments here will not apply any more. |
||||||
let r = | ||||||
self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?))?; | ||||||
// Check for exceeding shifts *even if* we cannot evaluate the LHS. | ||||||
if op == BinOp::Shr || op == BinOp::Shl { | ||||||
// We need the type of the LHS. We cannot use `place_layout` as that is the type | ||||||
// of the result, which for checked binops is not the same! | ||||||
let left_ty = left.ty(&self.local_decls, self.tcx); | ||||||
let left_size_bits = self.ecx.layout_of(left_ty).ok()?.size.bits(); | ||||||
let right_size = r.layout.size; | ||||||
let r_bits = r.to_scalar().ok(); | ||||||
// This is basically `force_bits`. | ||||||
let r_bits = r_bits.and_then(|r| r.to_bits_or_ptr(right_size, &self.tcx).ok()); | ||||||
if r_bits.map_or(false, |b| b >= left_size_bits as u128) { | ||||||
self.report_assert_as_lint( | ||||||
lint::builtin::ARITHMETIC_OVERFLOW, | ||||||
source_info, | ||||||
"this arithmetic operation will overflow", | ||||||
AssertKind::Overflow(op), | ||||||
)?; | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
// The remaining operators are handled through `overflowing_binary_op`. | ||||||
if self.use_ecx(|this| { | ||||||
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; | ||||||
let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; | ||||||
Ok(overflow) | ||||||
})? { | ||||||
self.report_assert_as_lint( | ||||||
lint::builtin::ARITHMETIC_OVERFLOW, | ||||||
source_info, | ||||||
"this arithmetic operation will overflow", | ||||||
AssertKind::Overflow(op), | ||||||
)?; | ||||||
if !left.needs_subst() { | ||||||
// The remaining operators are handled through `overflowing_binary_op`. | ||||||
if self.use_ecx(|this| { | ||||||
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; | ||||||
let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; | ||||||
Ok(overflow) | ||||||
})? { | ||||||
self.report_assert_as_lint( | ||||||
lint::builtin::ARITHMETIC_OVERFLOW, | ||||||
source_info, | ||||||
"this arithmetic operation will overflow", | ||||||
AssertKind::Overflow(op), | ||||||
)?; | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
Some(()) | ||||||
|
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.
Please avoid rightwards-drift, and instead do something like
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.
(Also applies to
check_binary_op
.)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.
Don't all of these
needs_subst
changes become obsolete with #71386 (comment)?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.
Yes.
I wrote these comments before you showed up in this PR.