-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Infinite recursion can be "optimized" into LLVM undef which produces a garbage value #18785
Comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Digging some more:
|
cc #17899 which would help paper over this bug. |
I think it's important that we publicly acknowledge the threat that this represents, given that it's going to be a known source of unsafety in safe Rust code as of 1.0. The fact that it's LLVM's fault doesn't get us off the hook, the least we can do is be forthright about it. |
Note that this causes pub fn f() -> isize { fn rec() -> isize { rec() } rec() } define i64 @_ZN1f20hfbe7b5e4892376b9eaaE() unnamed_addr #0 {
entry-block:
ret i64 undef
} |
@bstrie: There are hundreds of open LLVM bugs representing soundness issues, and this is just one. I don't think there's anything special about it. |
@mboeh: It's a well known bug in how LLVM models effects. It removes calls to pure functions when the result is unused and it doesn't model halting as part of purity. The work that needs to be done to fix it is known. There are lots of bugs in LLVM and they aren't going to remove an optimization simply because there's a fixable issue with it. |
You have a lot of writing to do if you want to mention each known soundness bug in LLVM in Rust documentation... note that Rust isn't hurt by this any more than other safe languages building on it. |
Triage: Today, this program stack overflows in debug mode, and prints |
The pub fn f(x: i32) -> i32 {
f(x)
}
; ModuleID = 'inf.0.rs'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: nounwind readnone uwtable
define i32 @_ZN1f20ha114846844acb461eaaE(i32) unnamed_addr #0 {
entry-block:
ret i32 undef
}
attributes #0 = { nounwind readnone uwtable } I wonder if we should turn it into a hard error, as it can be disastrous to dereference a garbage pointer: fn f(x: &String) -> &String { f(x) }
fn main() {
println!("{}", f(&String::from("abc")));
}
Such a crash is a violation of the language's guarantee stated here:
|
I agree that unconditional recursion should be turned into a hard error – as long as Rust does not guarantee tail call elimination unconditional recursion is always a bug (except when the function is intended to panic before overflowing the stack). |
It wouldn't be wholly unprecedented to have a lint which gradually progressed from allow to deny, but it would require an RFC. I suggest someone write one. :) |
Some system programs might want to issue the exit(2) syscall via
|
Functions may also call some other function with an infinite loop fn f(x: i32) -> String {
if 0 < x {
f(x - 1)
} else {
main_loop() // fn main_loop() -> !
}
} |
Closing in favor of #28728, which I believe is the same issue as this. |
I’ll reopen this issue, as the loop issue is somewhat separate from the recursion issue in terms of what parts of LLVM cause the issue to happen. I investigated the code reported in #54049 and found that there, starting with a code as seen below, will dataflow-propagate a constant value. define i32 @bar(i32 %i) unnamed_addr #0 {
start:
%0 = icmp slt i32 %i, 1000
br i1 %0, label %bb4, label %bb6
bb4: ; preds = %bb3
%1 = srem i32 %i, 2
%2 = call i32 @bar(i32 %1)
br label %bb6
bb6: ; preds = %bb4, %bb2
%_0.0 = phi i32 [ %2, %bb4 ], [ 1234567, %start ]
ret i32 %_0.0
}
attributes #0 = { uwtable } Running this code through the interprocedural sparse conditional constant propagation ( Unlike with loops, |
My bad. Adding a |
In that case, I don't think there's a bug everywhere outside of rustc - the code before xpSCCP is semantically equivalent to the code after it, and the recursive call removal occurs only because we lack |
My understanding, too, is that this is basically the same issue as #28728 (just modulo tail recursion optimization, which is definitely permitted) and the fix for the UB it causes is the same, inserting enough |
Note we get a stack overflow as expected(!?) with
-O0
(observation by mboeh)The text was updated successfully, but these errors were encountered: