Skip to content
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

floating point to integer casts can cause undefined behaviour #10184

Closed
thestinger opened this issue Oct 31, 2013 · 234 comments · Fixed by #71269
Closed

floating point to integer casts can cause undefined behaviour #10184

thestinger opened this issue Oct 31, 2013 · 234 comments · Fixed by #71269
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-medium Medium priority T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@thestinger
Copy link
Contributor

thestinger commented Oct 31, 2013

Status as of 2020-04-18

We intend to stabilize the saturating-float-casts behavior for as, and have stabilized unsafe library functions that handle the previous behavior. See #71269 for the latest discussion on that stabilization process.

Status as of 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:

  • One option is to take today's as cast behavior (which is UB in some cases) and add unsafe functions for the relevant types and such.
  • Another is to wait for LLVM to add a freeze concept which means that we get a garbage bit pattern, but it's at least not UB
  • Another is to implement casts via inline assembly in LLVM IR, as the current codegen is not heavily optimized.

Old 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:

If the value cannot fit in ty2, the results are undefined.

1.04E+17 as u8
@brson
Copy link
Contributor

brson commented Oct 31, 2013

Nominating

@pnkfelix
Copy link
Member

pnkfelix commented Nov 7, 2013

accepted for P-high, same reasoning as #10183

@pcwalton
Copy link
Contributor

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.

@pnkfelix
Copy link
Member

changing to P-high, same reasoning as #10183

@nrc
Copy link
Member

nrc commented Sep 12, 2014

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?

@pcwalton
Copy link
Contributor

We could add an intrinsic to LLVM that performs a "safe conversion". @zwarich may have other ideas.

@zwarich
Copy link

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.

@pcwalton
Copy link
Contributor

Oh, that's easy enough then.

@nrc
Copy link
Member

nrc commented Apr 23, 2015

ping @pnkfelix is this covered by the new overflow checking stuff?

@bluss
Copy link
Member

bluss commented May 7, 2015

These casts are not checked by rustc with debug assertions.

@Aatch
Copy link
Contributor

Aatch commented Sep 13, 2015

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.

@bluss
Copy link
Member

bluss commented Sep 13, 2015

This allows violating memory safety in safe rust, example from this forum post:

Undefs, huh? Undefs are fun. They tend to propagate. After a few minutes of wrangling..

#[inline(never)]
pub fn f(ary: &[u8; 5]) -> &[u8] {
    let idx = 1e100f64 as usize;
    &ary[idx..]
}

fn main() {
    println!("{}", f(&[1; 5])[0xdeadbeef]);
}

segfaults on my system (latest nightly) with -O.

@steveklabnik steveklabnik added the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label Oct 8, 2015
@steveklabnik
Copy link
Member

Marking with I-unsound given the violation of memory safety in safe rust.

@steveklabnik
Copy link
Member

@bluss , this does not segfualt for me, just gives an assertion error. untagging since i was the one who added it

@steveklabnik steveklabnik added I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness and removed I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness labels Oct 29, 2015
@steveklabnik
Copy link
Member

Sigh, I forgot the -O, re-tagging.

@nagisa
Copy link
Member

nagisa commented Feb 21, 2016

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.

@nikomatsakis nikomatsakis added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Feb 25, 2016
@nikomatsakis
Copy link
Contributor

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.

@RalfJung
Copy link
Member

RalfJung commented Apr 11, 2020

@sunfishcode thanks for updating! I have to translate the tests to Rust anyway so I still have to replace many things. ;)

Are the _sat tests any different in terms of the values being tested? (EDIT: there's a comment there saying the values are the same.) For Rust's saturating casts I took many of these values and added them in rust-lang/miri#1321. I was too lazy to do it for all of them... but I think this means that there is nothing to change right now with the updated file.

For the UB intrinsic, the traps on the wasm side should then become compile-fail tests in Miri I think.

@sunfishcode
Copy link
Member

The input values are all the same, the only difference is that _sat operators have expected output values on inputs where the trapping operators have expected traps.

@RalfJung
Copy link
Member

Tests for Miri (and thus also the Rust CTFE engine) were added in rust-lang/miri#1321. I locally checked that rustc -Zmir-opt-level=0 -Zsaturating-float-casts also passes the tests in that file.
I now also implemented the unchecked intrinsic in Miri, see rust-lang/miri#1325.

@Mark-Simulacrum
Copy link
Member

I've posted #71269 (comment) which documents the current state as I understood it and that PR also moves to stabilize the behavior of the saturating -Z flag.

Given the length of this thread I think if folks feel that I've missed anything in that comment, I would direct commentary to the PR, or, if it's minor, feel free to ping me on Zulip or Discord (simulacrum) and I can fix things up to avoid unnecessary noise on the PR thread.

I expect that someone on the language team will likely start an FCP proposal on that PR soon, and merging it will automatically close this issue out :)

@Mark-Simulacrum Mark-Simulacrum removed I-nominated E-help-wanted Call for participation: Help is requested to fix this issue. labels Apr 18, 2020
@federicomenaquintero
Copy link
Contributor

Are there plans for checked conversions? Something like fn i32::checked_from(f64) -> Result<i32, DoesntFit>?

@kennytm
Copy link
Member

kennytm commented Apr 22, 2020

You'll need to consider what should i32::checked_from(4.5) return.

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue May 5, 2020
…nikic

Define UB in float-to-int casts to saturate

This closes rust-lang#10184 by defining the behavior there to saturate infinities and values exceeding the integral range (on the lower or upper end). `NaN` is sent to zero.
@bors bors closed this as completed in 14d608f May 6, 2020
flip1995 pushed a commit to flip1995/rust that referenced this issue Jan 12, 2023
Allow implementing `Hash` with derived `PartialEq` (`derive_hash_xor_eq`

This is a common pattern and is totally allowed by the `Hash` trait.

Fixes rust-lang#2627

changelog: Move: Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`]
[rust-lang#10184](rust-lang/rust-clippy#10184)
changelog: Enhancement: [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash` implementations
[rust-lang#10184](rust-lang/rust-clippy#10184)
<!-- changelog_checked -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-medium Medium priority T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.