Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upfloating point to integer casts can cause undefined behaviour #10184
Comments
This comment has been minimized.
This comment has been minimized.
|
Nominating |
thestinger
referenced this issue
Nov 3, 2013
Open
Audit for binary IEEE 754-2008 compliance on relevant platforms #10087
This comment has been minimized.
This comment has been minimized.
|
accepted for P-high, same reasoning as #10183 |
pnkfelix
referenced this issue
Nov 7, 2013
Closed
integer to floating point casts can cause undefined behaviour #10185
This comment has been minimized.
This comment has been minimized.
|
I don't think this is backwards incompatible at a language level. It will not cause code that was working OK to stop working. Nominating. |
This comment has been minimized.
This comment has been minimized.
|
changing to P-high, same reasoning as #10183 |
huonw
referenced this issue
Jul 8, 2014
Closed
floating point to floating point casts have undefined behaviour #15536
This comment has been minimized.
This comment has been minimized.
|
How do we propose to solve this and #10185? Since whether behaviour is defined or not depends on the dynamic value of the number being cast, it seems the only solution is to insert dynamic checks. We seem to agree we do not want to do that for arithmetic overflow, are we happy to do it for cast overflow? |
This comment has been minimized.
This comment has been minimized.
|
We could add an intrinsic to LLVM that performs a "safe conversion". @zwarich may have other ideas. |
This comment has been minimized.
This comment has been minimized.
zwarich
commented
Sep 12, 2014
|
AFAIK the only solution at the moment is to use the target-specific intrinsics. That's what JavaScriptCore does, at least according to someone I asked. |
This comment has been minimized.
This comment has been minimized.
|
Oh, that's easy enough then. |
This comment has been minimized.
This comment has been minimized.
|
ping @pnkfelix is this covered by the new overflow checking stuff? |
This comment has been minimized.
This comment has been minimized.
|
These casts are not checked by rustc with debug assertions. |
arielb1
referenced this issue
Sep 13, 2015
Closed
Float to integer casts can have undefined results #28382
This comment has been minimized.
This comment has been minimized.
|
I'm happy to handle this, but I need a concrete solution. I personally think that it should be checked along with overflowing integer arithmetic, as it's a very similar issue. I don't really mind what we do though. Note that this issue is currently causing an ICE when used in certain constant expressions. |
This comment has been minimized.
This comment has been minimized.
|
This allows violating memory safety in safe rust, example from this forum post:
#[inline(never)]
pub fn f(ary: &[u8; 5]) -> &[u8] {
let idx = 1e100f64 as usize;
&ary[idx..]
}
fn main() {
println!("{}", f(&[1; 5])[0xdeadbeef]);
}
|
steveklabnik
added
the
I-unsound 💥
label
Oct 8, 2015
This comment has been minimized.
This comment has been minimized.
|
Marking with I-unsound given the violation of memory safety in safe rust. |
This comment has been minimized.
This comment has been minimized.
|
@bluss , this does not segfualt for me, just gives an assertion error. untagging since i was the one who added it |
steveklabnik
added
I-unsound 💥
and removed
I-unsound 💥
labels
Oct 29, 2015
This comment has been minimized.
This comment has been minimized.
|
Sigh, I forgot the -O, re-tagging. |
simonbyrne
referenced this issue
Jan 6, 2016
Closed
convert(Int16, ::Float64) does not throw inexact exception when it should #14549
This comment has been minimized.
This comment has been minimized.
|
re-nominating for P-high. Apparently this was at some point P-high but got lower over time. This seems pretty important for correctness. EDIT: didn’t react to triage comment, adding label manually. |
nagisa
added
the
I-nominated
label
Feb 21, 2016
nikomatsakis
added
T-compiler
T-lang
labels
Feb 25, 2016
This comment has been minimized.
This comment has been minimized.
|
It seems like the precedent from the overflow stuff (e.g. for shifting) is to just settle on some behavior. Java seems to produce the result modulo the range, which seems not unreasonable; I'm not sure just what kind of LLVM code we'd need to handle that. |
This comment has been minimized.
This comment has been minimized.
|
@kennytm Did we expect LLVM 6 to change something? Are they discussing a particular enhancement that would benefit this use case? If so, what's the ticket number? |
This comment was marked as outdated.
This comment was marked as outdated.
|
@insanitybit It... appears to still be open...? |
This comment was marked as resolved.
This comment was marked as resolved.
insanitybit
commented
Jul 20, 2018
•
|
Welp, no clue what I was looking at. Thanks! |
This comment has been minimized.
This comment has been minimized.
|
@rkruppe didn't we ensure that float to int casts are no longer UB in LLVM
(by changing docs)?
On Jul 20, 2018 4:31 AM, "Colin" <notifications@github.com> wrote:
Welp, no clue what I was looking at.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#10184 (comment)>,
or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AApc0v3rJHhZMD7Kv7RC8xkGOiIhkGB1ks5uITMHgaJpZM4BJ45C>
.
|
This comment has been minimized.
This comment has been minimized.
|
@nagisa Maybe you’re thinking of This issue is about |
This comment has been minimized.
This comment has been minimized.
|
@nagisa You might be thinking of float->float casts, see #15536 and rust-lang-nursery/nomicon#65. |
This comment has been minimized.
This comment has been minimized.
|
Ah, yes, that was float to float.
…On Fri, Jul 20, 2018, 12:24 Robin Kruppe ***@***.***> wrote:
@nagisa <https://github.com/nagisa> You might be thinking of float->float
casts, see #15536 <#15536> and
rust-lang-nursery/nomicon#65
<rust-lang-nursery/nomicon#65>.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#10184 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AApc0gA24Hz8ndnYhRXCyacd3HdUSZjYks5uIaHegaJpZM4BJ45C>
.
|
rkruppe
referenced this issue
Aug 17, 2018
Open
Conversions: `FromLossy` and `TryFromLossy` traits #2484
This comment has been minimized.
This comment has been minimized.
|
LLVM 7 release notes mention something:
Does that have any bearing on this issue? |
This comment has been minimized.
This comment has been minimized.
|
We shouldn't care what LLVM does for overflowing casts, as long as it isn't unsafe undefined behavior. The result can be garbage as long as it can't cause unsound behavior. |
This comment has been minimized.
This comment has been minimized.
Not really. The UB did not change, LLVM got even more aggressive about exploiting it, which makes it easier to be affected by it in practice, but the soundness issue is unchanged. In particular, the new attribute does not remove the UB or affect any optimizations that existed before LLVM 7. |
rkruppe
referenced this issue
Oct 2, 2018
Open
LLVM loop optimization can make safe programs crash #28728
This comment has been minimized.
This comment has been minimized.
|
@rkruppe out of curiosity, has this sort of fallen by the wayside? It seems like https://internals.rust-lang.org/t/help-us-benchmark-saturating-float-casts/6231/14 went well enough and the implementation hasn't had too many bugs. It seems that a slight performance regression was always expected, but to compile correctly seems like a worthwhile tradeoff. Is this just waiting to be pushed across the finish line? Or are there other known blockers? |
This comment has been minimized.
This comment has been minimized.
|
Mostly I've been distracted / busy with other things, but a x0.82 regression in RBG JPEG encoding seems more than "slight", a rather bitter pill to swallow (although it's reassuring that other kinds of workload don't seem affected). It's not severe enough that I would object to turning saturation on by default, but enough that I'm hesitant to push for it myself before we've tried the "also provide a conversion function that's faster than saturation but may generate (safe) garbage" option discussed before. I haven't gotten to that, and apparently nobody else has either, so this has fallen by the wayside. |
This comment has been minimized.
This comment has been minimized.
|
Ok cool thanks for the update @rkruppe! I'm curious though if there's actually an implementation of the safe garbage option? I could imagine us easily providing something like |
This comment has been minimized.
This comment has been minimized.
|
There's no An |
This comment has been minimized.
This comment has been minimized.
|
There's also significant room for improvement in the safe saturating float-to-int sequence. LLVM today doesn't have anything specifically for this, but if inline asm solutions are on the table, it wouldn't be difficult to do something like this: cvttsd2si %xmm0, %eax # x86's cvttsd2si returns 0x80000000 on overflow and invalid cases
cmp $1, %eax # a compact way to test whether %eax is equal to 0x80000000
jno ok
... # slow path: check for and handle overflow and invalid cases
ok:which should be significantly faster than what rustc currently does. |
This comment has been minimized.
This comment has been minimized.
|
Ok I just wanted to be sure to clarify, thanks! I figured that inline asm solutions aren't workable as defaults as it'd inhibit other optimizations too much, but I haven't tried out myself. I'd personally prefer that we close this unsound hole by defining some reasonable behavior (like exactly today's saturating casts). If necessary we can always preserve today's fast/unsound implementation as an unsafe function, and in the limit of time given infinite resources we can even drastically improve the default and/or add other specialized conversion functions (like a safe conversion where out of bounds isn't UB but just a garbage bit pattern) Would others be opposed to such a strategy? Do we think that this isn't important enough to fix in the meantime? |
This comment has been minimized.
This comment has been minimized.
|
I think inline assembly should be tolerable for
I do not object to flipping on saturating now and possibly adding alternatives later, I just don't want to be the one who has to drum up the consensus for it and justify it to users whose code got slower |
This comment has been minimized.
This comment has been minimized.
|
I've started some work to implement intrinsics for saturating float to int casts in LLVM: https://reviews.llvm.org/D54749 If that goes anywhere, it will provide a relatively low-overhead way of getting the saturating semantics. |
bluss
referenced this issue
Dec 13, 2018
Closed
f32 to u32 cast has different result in different context #56782
cuviper
referenced this issue
Dec 14, 2018
Open
Implement Power10 trait, with first method is_power_of_ten #15
This comment has been minimized.
This comment has been minimized.
AaronM04
commented
Mar 1, 2019
|
How does one reproduce this undefined behavior? I tried the example in the comment but the result was println!("{}", 1.04E+17 as u8); |
This comment has been minimized.
This comment has been minimized.
|
Undefined behavior can't be reliably observed that way, sometimes it gives you what you expect but in more complex situations breaks down. In short, the code generation engine (LLVM) we use is allowed to assume that this doesn't happen, and thus it may generate bad code if it ever relies on this assumption. |
This comment has been minimized.
This comment has been minimized.
lovasoa
commented
Mar 15, 2019
•
|
@AaronM04 an example of reproducible undefined behavior was posted on reddit today: fn main() {
let a = 360.0f32;
println!("{}", a as u8);
let a = 360.0f32 as u8;
println!("{}", a);
println!("{}", 360.0f32 as u8);
}(see playground) |
This comment has been minimized.
This comment has been minimized.
|
I assume that last comment was meant for @AaronM04, in reference to their previous comment. |

thestinger commentedOct 31, 2013
•
edited by alexcrichton
Current status (2018-11-05)
A flag has been implemented in the compiler,
-Zsaturating-float-casts, which will cause all float to integer casts have "saturating" behavior where if it's out of bounds it's clamped to the nearest bound. A call for benchmarking of this change went out awhile ago. Results, while positive in many projects, are quite negative for some projects and indicates that we're not done here.The next steps are figuring out how to recover performance for these cases:
ascast behavior (which is UB in some cases) and addunsafefunctions for the relevant types and such.freezeconcept which means that we get a garbage bit pattern, but it's at least not UBOld status
UPDATE (by @nikomatsakis): After much discussion, we've got the rudiments of a plan for how to address this problem. But we need some help with actually investigating the performance impact and working out the final details!
ORIGINAL ISSUE FOLLOWS: