diff --git a/util/deephash/deephash.go b/util/deephash/deephash.go index 507229ac3..10667b0e2 100644 --- a/util/deephash/deephash.go +++ b/util/deephash/deephash.go @@ -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() } diff --git a/util/deephash/deephash_test.go b/util/deephash/deephash_test.go index f806c4559..42cf00f54 100644 --- a/util/deephash/deephash_test.go +++ b/util/deephash/deephash_test.go @@ -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) } } }