Skip to content

Commit 8c5c87b

Browse files
committed
util/deephash: fix collisions between different types
Updates tailscale#4883 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
1 parent 4f6fa3d commit 8c5c87b

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

util/deephash/deephash.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,18 @@ func Hash(v any) (s Sum) {
127127
h.reset()
128128
seedOnce.Do(initSeed)
129129
h.hashUint64(seed)
130-
h.hashValue(reflect.ValueOf(v), false)
130+
131+
rv := reflect.ValueOf(v)
132+
if rv.IsValid() {
133+
// Always treat the Hash input as an interface (it is), including hashing
134+
// its type, otherwise two Hash calls of different types could hash to the
135+
// same bytes off the different types and get equivalent Sum values. This is
136+
// the same thing that we do for reflect.Kind Interface in hashValue, but
137+
// the initial reflect.ValueOf from an interface value effectively strips
138+
// the interface box off so we have to do it at the top level by hand.
139+
h.hashType(rv.Type())
140+
h.hashValue(rv, false)
141+
}
131142
return h.sum()
132143
}
133144

util/deephash/deephash_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func TestHash(t *testing.T) {
6161
}
6262
type MyBool bool
6363
type MyHeader tar.Header
64+
var zeroFloat64 float64
6465
tests := []struct {
6566
in tuple
6667
wantEq bool
@@ -102,6 +103,10 @@ func TestHash(t *testing.T) {
102103
{in: tuple{iface{&MyHeader{}}, iface{&tar.Header{}}}, wantEq: false},
103104
{in: tuple{iface{[]map[string]MyBool{}}, iface{[]map[string]MyBool{}}}, wantEq: true},
104105
{in: tuple{iface{[]map[string]bool{}}, iface{[]map[string]MyBool{}}}, wantEq: false},
106+
{in: tuple{zeroFloat64, -zeroFloat64}, wantEq: false}, // Issue 4883 (false alarm)
107+
{in: tuple{[]any(nil), 0.0}, wantEq: false}, // Issue 4883
108+
{in: tuple{[]any(nil), uint8(0)}, wantEq: false}, // Issue 4883
109+
{in: tuple{nil, nil}, wantEq: true}, // Issue 4883
105110
{
106111
in: func() tuple {
107112
i1 := 1
@@ -117,7 +122,7 @@ func TestHash(t *testing.T) {
117122
for _, tt := range tests {
118123
gotEq := Hash(tt.in[0]) == Hash(tt.in[1])
119124
if gotEq != tt.wantEq {
120-
t.Errorf("(Hash(%v) == Hash(%v)) = %v, want %v", tt.in[0], tt.in[1], gotEq, tt.wantEq)
125+
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)
121126
}
122127
}
123128
}

0 commit comments

Comments
 (0)