Skip to content

overlong bit shifts cause undefined behaviour #10183

Closed
@thestinger

Description

@thestinger
Contributor
1 >> 24353125315

Activity

nikomatsakis

nikomatsakis commented on Oct 31, 2013

@nikomatsakis
Contributor

At some point we discussed this in a meeting and decided it was acceptable.

thestinger

thestinger commented on Oct 31, 2013

@thestinger
ContributorAuthor

@nikomatsakis: it's not just an unspecified result as I previously thought though, it breaks memory safety

nikomatsakis

nikomatsakis commented on Oct 31, 2013

@nikomatsakis
Contributor

Do you have a concrete example where this leads to breaking memory safety? I can imagine how it might theoretically happen (that is, the behavior of the program as a whole is technically undefined), but I'm not sure that it actually does? I guess I can imagine some compiler saying that "since i1 >> i2 implies i2 < 32, then I'll do some wacky optimization to some expression involving i2..."

thestinger

thestinger commented on Oct 31, 2013

@thestinger
ContributorAuthor

@nikomatsakis: LLVM won't reserve a register for the return value, and will just read arbitrary registers on a read of the value, so for example a bounds check could fail to work

nikomatsakis

nikomatsakis commented on Oct 31, 2013

@nikomatsakis
Contributor

@thestinger return value of what function? read of what value? Can you spell it out more?

thestinger

thestinger commented on Oct 31, 2013

@thestinger
ContributorAuthor

For example, a pattern like this:

let mut xs = [1, 2, 3];
let index = 1 >> 128; // this is `undef`
xs[index] = 100;

The indexing operation will essentially expand to this:

if index < 3 {
    *unsafe_index(xs, index) = 100;
} else {
    fail_bounds_check(...);
}

The index value as read for the comparison may be different than the value passed to unsafe_index.

http://llvm.org/docs/LangRef.html#undefined-values

In practice, it likely won't be in most cases, but in a more complex example where there was register pressure it would be.

thestinger

thestinger commented on Oct 31, 2013

@thestinger
ContributorAuthor

The key part:

an ‘undef‘ “variable” can arbitrarily change its value over its “live range”. This is true because the variable doesn’t actually have a live range. Instead, the value is logically read from arbitrary registers that happen to be around when needed, so the value is not necessarily consistent over time.

brson

brson commented on Oct 31, 2013

@brson
Contributor

Nominating.

nikomatsakis

nikomatsakis commented on Oct 31, 2013

@nikomatsakis
Contributor

I see. Fascinating! Is there a way to "capture" an undefined value and make it constant? For example, storing it to a stack slot and reading it back? (Presumably this load/store could be optimized away, so prob not). If so, we could capture the output of shift operations in this way.

pnkfelix

pnkfelix commented on Nov 7, 2013

@pnkfelix
Member

Accepted for P-backcompat lang. There are a variety of ways to deal with this. We need to choose one. Will discuss further (e.g. in tues mtg).

pcwalton

pcwalton commented on Nov 22, 2013

@pcwalton
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

pnkfelix commented on Dec 19, 2013

@pnkfelix
Member

changing priority to P-high. not assigning to 1.0 milestone.

27 remaining items

thestinger

thestinger commented on Feb 17, 2015

@thestinger
ContributorAuthor

This is a memory safety hole in the implementation. It's an issue regardless of how the language is intended to work per RFCs...

srh

srh commented on Feb 17, 2015

@srh

@steveklabnik How does the overflow RFC address this?

steveklabnik

steveklabnik commented on Feb 17, 2015

@steveklabnik
Member

Sorry, by 'address' I mean a decision was made, and I'll be documenting the result. Not that it's any different than today.

thestinger

thestinger commented on Feb 17, 2015

@thestinger
ContributorAuthor

This bug isn't asking for a change in semantics, it's filed to cover a known memory safety hole in the language. The fact that a clear solution has been specified doesn't mean the bug is fixed... pretending memory safety holes don't exist in the implementation doesn't make them go away.

Gankra

Gankra commented on Feb 17, 2015

@Gankra
Contributor

Agreed this issue should not be closed until UB in safe Rust does not occur.

reopened this on Feb 17, 2015
ben0x539

ben0x539 commented on Feb 17, 2015

@ben0x539
Contributor

I don't think the RFC addresses this issue at all. Under "unresolved questions", it lists "shifts by an excessive number of bits". It just proposes debug assertions and doesn't prevent undefined behavior in release builds.

pnkfelix

pnkfelix commented on Mar 20, 2015

@pnkfelix
Member

Here is a variant of @ruud-v-a 's example that assert fails (in different places) in both -O0 and -O2 modes. (It was adapted from #23551.)

#![allow(exceeding_bitshifts)]

fn id<T>(x: T) -> T { x }

fn main() {
    let sh = 32;
    assert_eq!(0, 1_i32 << 32);     // note that this line "is the same" as the two below
    assert_eq!(0, 1_i32 << sh);     // fails here with -O0
    assert_eq!(0, 1_i32 << id(sh)); // fails here with -O2
}

playpen

pnkfelix

pnkfelix commented on Mar 20, 2015

@pnkfelix
Member

@ben0x539 I believe the intent of the RFC is to ensure that the fallback behaviors, when reasonable, do not expose undefined behavior from LLVM.

In these cases, it seems like the fallbacks should simply mask-out the higher order bits as appropriate for each type.

added a commit that references this issue on Mar 20, 2015
19fef72
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @ben0x539@steveklabnik@brson@nikomatsakis@pcwalton

        Issue actions

          overlong bit shifts cause undefined behaviour · Issue #10183 · rust-lang/rust