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

derive(Hash) on enum causes hash collision on 32-bit system #21714

Closed
klutzy opened this issue Jan 28, 2015 · 8 comments
Closed

derive(Hash) on enum causes hash collision on 32-bit system #21714

klutzy opened this issue Jan 28, 2015 · 8 comments
Labels
A-syntaxext Area: Syntax extensions

Comments

@klutzy
Copy link
Contributor

klutzy commented Jan 28, 2015

use std::hash::{hash, SipHasher};

#[derive(Hash)]
enum E {
    A = 1,
    B,
}

fn main() {
    let a = hash::<_,   SipHasher>(&E::A);
    let b = hash::<_,   SipHasher>(&E::B);

    println!("{}, {}, {}", a, b, a == b);
}
$ rustc a.rs --target=i686-unknown-linux-gnu && ./a
13715208377448023093, 13715208377448023093, true

Output of -Z unstable-options --pretty=expanded is:

// snip
#[automatically_derived]
impl <__S: ::std::hash::Writer + ::std::hash::Hasher> ::std::hash::Hash<__S>
 for E {
    #[inline]
    fn hash(&self, __arg_0: &mut __S) -> () {
        match (&*self,) {
            (&E::A,) => { ::std::hash::Hash::hash(&1, __arg_0); }
            (&E::B,) => { ::std::hash::Hash::hash(&1us, __arg_0); }
        }
    }
}
// snip

Here A uses 1 (is i32) and B uses 1us for hashing, thus they collide if they have same bit representation.

(was potential issue of #18573)

@steveklabnik
Copy link
Member

These APIs have changed a bunch. I think this is the situation today:

use std::hash::Hasher;

#[derive(Hash)]
enum E {
    A = 1,
    B,
}

fn main() {
    let mut hasher = SipHasher::new();

    hasher.write_i32(E::A as i32);
    let a = hasher.finish();

    let mut hasher = SipHasher::new();
    hasher.write_i32(E::B as i32);
    let b = hasher.finish();

    println!("{:?}, {:?}, {:?}", a, b, a == b);
}

I don't have --target=i686-unknown-linux-gnu handy... however, expanding still gives

impl ::std::hash::Hash for E {
    fn hash<__H: ::std::hash::Hasher>(&self, __arg_0: &mut __H) -> () {
        match (&*self,) {
            (&E::A,) => { ::std::hash::Hash::hash(&1, __arg_0); }
            (&E::B,) => { ::std::hash::Hash::hash(&1usize, __arg_0); }
        }
    }
}

the 1 and 1usize are still there, so I'm assuming the issue is.

@ranma42
Copy link
Contributor

ranma42 commented Nov 6, 2015

cc @ranma42

@apasel422
Copy link
Contributor

This can be addressed the same way as #24270.

@durka
Copy link
Contributor

durka commented Mar 5, 2016

This seems to be fixed: http://is.gd/tm84D2 (the variants no longer have the same discriminant)

@apasel422
Copy link
Contributor

Isn't that a 64-bit system?

On Saturday, March 5, 2016, Alex Burka notifications@github.com wrote:

This seems to be fixed: http://is.gd/tm84D2


Reply to this email directly or view it on GitHub
#21714 (comment).

@durka
Copy link
Contributor

durka commented Mar 5, 2016

Yes, but it seems like the real bug in the original was enum E { A = 1, B } turning into enum E { A = 1, B = 1} or am I misunderstanding?

@apasel422
Copy link
Contributor

I think it's that in the syntax expansion, one's hash is 1i32 and the
other's is 1isize.

On Saturday, March 5, 2016, Alex Burka notifications@github.com wrote:

Yes, but it seems like the real bug in the original was enum E { A = 1, B
} turning into enum E { A = 1, B = 1} or am I misunderstanding?


Reply to this email directly or view it on GitHub
#21714 (comment).

@durka
Copy link
Contributor

durka commented Mar 7, 2016

@apasel422 I understand now. Working on a fix.

durka added a commit to durka/rust that referenced this issue Mar 10, 2016
durka added a commit to durka/rust that referenced this issue Mar 10, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
durka added a commit to durka/rust that referenced this issue Mar 10, 2016
durka added a commit to durka/rust that referenced this issue Mar 10, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
Manishearth added a commit to Manishearth/rust that referenced this issue Mar 12, 2016
cleanups and fixes for #[derive]

This contains a bunch of little cleanups and fixes to `#[derive]`. There are more extensive comments on each individual commit.

- hygiene cleanups
- use `discriminant_value` instead of variant index in `#[derive(Hash)]`
- ~~don't move out of borrowed content in `#[derive(PartialOrd, PartialEq)]`~~
- use `intrinsics::unreachable()` instead of `unreachable!()`

I don't believe there are any breaking changes in here, but I do want some more eyes on that.

Fixes rust-lang#2810 (!), I believe (we still assume that "std" or "core" is the standard library but so does the rest of rustc...).
Fixes rust-lang#21714 (cc @apasel422).
~~Fixes~~ (postponed) rust-lang#24047 (cc @withoutboats @bluss).
Fixes rust-lang#31714 (cc @alexcrichton @bluss).
Fixes rust-lang#31886 (cc @oli-obk).
durka added a commit to durka/rust that referenced this issue Mar 13, 2016
durka added a commit to durka/rust that referenced this issue Mar 13, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
bors added a commit that referenced this issue Mar 13, 2016
cleanups and fixes for #[derive]

This contains a bunch of little cleanups and fixes to `#[derive]`. There are more extensive comments on each individual commit.

- hygiene cleanups
- use `discriminant_value` instead of variant index in `#[derive(Hash)]`
- ~~don't move out of borrowed content in `#[derive(PartialOrd, PartialEq)]`~~
- use `intrinsics::unreachable()` instead of `unreachable!()`

I don't believe there are any breaking changes in here, but I do want some more eyes on that.

Fixes #2810 (!), I believe (we still assume that "std" or "core" is the standard library but so does the rest of rustc...).
Fixes #21714 (cc @apasel422).
~~Fixes~~ (postponed) #24047 (cc @withoutboats @bluss).
Fixes #31714 (cc @alexcrichton @bluss).
Fixes #31886 (cc @oli-obk).
durka added a commit to durka/rust that referenced this issue Mar 22, 2016
durka added a commit to durka/rust that referenced this issue Mar 22, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
durka added a commit to durka/rust that referenced this issue Mar 27, 2016
durka added a commit to durka/rust that referenced this issue Mar 27, 2016
This is the same approach taken in rust-lang#24270, except that this
should not be a breaking change because it only changes the output
of hash functions, which nobody should be relying on.
bors added a commit that referenced this issue Mar 27, 2016
derive: use discriminant_value in #[derive(Hash)]

derive: use discriminant_value in #[derive(Hash)]

Fixes #21714.

Spawned from #32139.

r? @alexcrichton
bors added a commit that referenced this issue Mar 27, 2016
derive: use discriminant_value in #[derive(Hash)]

derive: use discriminant_value in #[derive(Hash)]

Fixes #21714.

Spawned from #32139.

r? @alexcrichton
Manishearth added a commit to Manishearth/rust that referenced this issue Mar 27, 2016
derive: use discriminant_value in #[derive(Hash)]

derive: use discriminant_value in #[derive(Hash)]

Fixes rust-lang#21714.

Spawned from rust-lang#32139.

r? @alexcrichton
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-syntaxext Area: Syntax extensions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants