util/deephash: fix collisions between different types

Updates #4883

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/4848/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent 4f6fa3d63a
commit 8c5c87be26

@ -127,7 +127,18 @@ func Hash(v any) (s Sum) {
h.reset()
seedOnce.Do(initSeed)
h.hashUint64(seed)
h.hashValue(reflect.ValueOf(v), false)
rv := reflect.ValueOf(v)
if rv.IsValid() {
// Always treat the Hash input as an interface (it is), including hashing
// its type, otherwise two Hash calls of different types could hash to the
// same bytes off the different types and get equivalent Sum values. This is
// the same thing that we do for reflect.Kind Interface in hashValue, but
// the initial reflect.ValueOf from an interface value effectively strips
// the interface box off so we have to do it at the top level by hand.
h.hashType(rv.Type())
h.hashValue(rv, false)
}
return h.sum()
}

@ -61,6 +61,7 @@ func TestHash(t *testing.T) {
}
type MyBool bool
type MyHeader tar.Header
var zeroFloat64 float64
tests := []struct {
in tuple
wantEq bool
@ -102,6 +103,10 @@ func TestHash(t *testing.T) {
{in: tuple{iface{&MyHeader{}}, iface{&tar.Header{}}}, wantEq: false},
{in: tuple{iface{[]map[string]MyBool{}}, iface{[]map[string]MyBool{}}}, wantEq: true},
{in: tuple{iface{[]map[string]bool{}}, iface{[]map[string]MyBool{}}}, wantEq: false},
{in: tuple{zeroFloat64, -zeroFloat64}, wantEq: false}, // Issue 4883 (false alarm)
{in: tuple{[]any(nil), 0.0}, wantEq: false}, // Issue 4883
{in: tuple{[]any(nil), uint8(0)}, wantEq: false}, // Issue 4883
{in: tuple{nil, nil}, wantEq: true}, // Issue 4883
{
in: func() tuple {
i1 := 1
@ -117,7 +122,7 @@ func TestHash(t *testing.T) {
for _, tt := range tests {
gotEq := Hash(tt.in[0]) == Hash(tt.in[1])
if gotEq != tt.wantEq {
t.Errorf("(Hash(%v) == Hash(%v)) = %v, want %v", tt.in[0], tt.in[1], gotEq, tt.wantEq)
t.Errorf("(Hash(%T %v) == Hash(%T %v)) = %v, want %v", tt.in[0], tt.in[0], tt.in[1], tt.in[1], gotEq, tt.wantEq)
}
}
}

Loading…
Cancel
Save