-
Notifications
You must be signed in to change notification settings - Fork 384
Make sure interpreter errors are never discarded #3855
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
Comments
drop bombs to discourage misuse? |
Yeah, maybe that works. |
I actually encountered this a few times so I'd really appreciate this enhancement, here is how I usually encounter this, hope it would help to produce a clearer diagnostic/ a test if anyone would like to work on this: This usually happen when I forgot to put a match offset {
None => fd.read(&fd, communicate, buf, count, dest, this), // the `?` is missing here
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
this.write_int(-1, dest)?;
return Ok(());
};
fd.pread(communicate, offset, buf, count, dest, this) // the `?` is missing here
}
}; ( At this point, the compiler will emit an error and suggestion: warning: unused `Result` that must be used
--> src/shims/unix/fd.rs:587:9
|
587 | / match offset {
588 | | None => fd.read(&fd, communicate, buf, count, dest, this),
589 | | Some(offset) => {
590 | | let Ok(offset) = u64::try_from(offset) else {
... |
597 | | }
598 | | };
| |_________^
|
= note: this `Result` may be an `Err` variant, which should be handled
= note: `#[warn(unused_must_use)]` on by default
help: use `let _ = ...` to ignore the resulting value
|
587 | let _ = match offset {
| +++++++ So if I didn't examine the suggestion carefully, I would just directly apply the suggestion and leads to: let _ = match offset { // the change is the `let _ = here`
None => fd.read(&fd, communicate, buf, count, dest, this),
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
this.write_int(-1, dest)?;
return Ok(());
};
fd.pread(communicate, offset, buf, count, dest, this)
}
}; After the change, it will compile successfully. IMO this error is pretty dangerous as it can only be caught manually or by certain test, and the test error is not helpful for debugging as it points to different direction ( related discussion in zulip ) The correct fix for the initial code is actually: match offset {
None => fd.read(&fd, communicate, buf, count, dest, this)?, // Added a `? `here
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
this.write_int(-1, dest)?;
return Ok(());
};
fd.pread(communicate, offset, buf, count, dest, this)? // Added a `?` here
}
}; |
Yeah IMO that's a dangerous suggestion to make. |
FWIW diagnostics and compilation errors in rustc work in a similar fashion, and dropping one without emitting it will ICE ultimately via Just make sure to check if you're already panicking to avoid a double panic abort. |
Unfortunately, panic-on-drop doesn't work. We have users like GVN that call interpreter methods like this let lhs = self.evaluated[lhs].as_ref()?;
let lhs = self.ecx.read_immediate(lhs).ok()?;
let rhs = self.evaluated[rhs].as_ref()?;
let rhs = self.ecx.read_immediate(rhs).ok()?;
let val = self.ecx.binary_op(bin_op, &lhs, &rhs).ok()?; So any error is immediately dropped and turned into an However, it also seems entirely possible that one interpreter triggers a query that triggers const-eval that leads to another interpreter instance being created, possibly on the same thread. So TLS won't work either. So... the next best thing I can think of is to use an extension trait to add a |
This works, you take out the old value, set your own and at the end when you check the value, you set the old value again. |
I went with the extension trait method: rust-lang/rust#130885. |
…li-obk panic when an interpreter error gets unintentionally discarded One important invariant of Miri is that when an interpreter error is raised (*in particular* a UB error), those must not be discarded: it's not okay to just check `foo().is_err()` and then continue executing. This seems to catch new contributors by surprise fairly regularly, so this PR tries to make it so that *if* this ever happens, we get a panic rather than a silent missed UB bug. The interpreter error type now contains a "guard" that panics on drop, and that is explicitly passed to `mem::forget` when an error is deliberately discarded. Fixes rust-lang/miri#3855
Rollup merge of rust-lang#130885 - RalfJung:interp-error-discard, r=oli-obk panic when an interpreter error gets unintentionally discarded One important invariant of Miri is that when an interpreter error is raised (*in particular* a UB error), those must not be discarded: it's not okay to just check `foo().is_err()` and then continue executing. This seems to catch new contributors by surprise fairly regularly, so this PR tries to make it so that *if* this ever happens, we get a panic rather than a silent missed UB bug. The interpreter error type now contains a "guard" that panics on drop, and that is explicitly passed to `mem::forget` when an error is deliberately discarded. Fixes rust-lang/miri#3855
panic when an interpreter error gets unintentionally discarded One important invariant of Miri is that when an interpreter error is raised (*in particular* a UB error), those must not be discarded: it's not okay to just check `foo().is_err()` and then continue executing. This seems to catch new contributors by surprise fairly regularly, so this PR tries to make it so that *if* this ever happens, we get a panic rather than a silent missed UB bug. The interpreter error type now contains a "guard" that panics on drop, and that is explicitly passed to `mem::forget` when an error is deliberately discarded. Fixes rust-lang#3855
I hit this error today, and I really like this change! Preparing a sysroot for Miri (target: x86_64-unknown-linux-gnu)... done
thread 'rustc' panicked at compiler/rustc_middle/src/mir/interpret/error.rs:745:13:
an interpreter error got improperly discarded; use `discard_err()` if this is intentional |
One important invariant of Miri is that when an interpreter error is raised (in particular a UB error), those must not be discarded: it's not okay to just check
foo().is_err()
and then continue executing.This seems to catch new contributors by surprise fairly regularly. Would be good to make sure this can never happen. Ideally we'd have some sort of static analysis for this, but I can't think of an easy way to do that (could be an interesting clippy lint). The next best thing is to enforce this dynamically. The problem is that
InterpError
creation does not have access to theInterpCx
. So instead we'd need some thread-local state to indicate "this interpreter is busted, don't continue executing in it... and we'd have to hope that nobody creates two interpreter instances on the same thread... but I can't think of a better way right now.The text was updated successfully, but these errors were encountered: