util/deephash: fix unexported time.Time hashing

Updates tailscale/corp#6311

Change-Id: I33cd7e4040966261c2f2eb3d32f29936aeb7f632
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/5193/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent 51c3d74095
commit 04cf46a762

@ -544,9 +544,11 @@ func (h *hasher) hashComplex128v(v reflect.Value) bool {
func (h *hasher) hashTimev(v reflect.Value) bool { func (h *hasher) hashTimev(v reflect.Value) bool {
var t time.Time var t time.Time
if v.CanAddr() { if v.CanAddr() {
t = *(v.Addr().Interface().(*time.Time)) t = *(*time.Time)(v.Addr().UnsafePointer())
} else { } else if v.CanInterface() {
t = v.Interface().(time.Time) t = v.Interface().(time.Time)
} else {
return false
} }
b := t.AppendFormat(h.scratch[:1], time.RFC3339Nano) b := t.AppendFormat(h.scratch[:1], time.RFC3339Nano)
b[0] = byte(len(b) - 1) // more than sufficient width; if not, good enough. b[0] = byte(len(b) - 1) // more than sufficient width; if not, good enough.

@ -28,6 +28,7 @@ import (
"tailscale.com/types/ipproto" "tailscale.com/types/ipproto"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/structs" "tailscale.com/types/structs"
"tailscale.com/util/deephash/testtype"
"tailscale.com/util/dnsname" "tailscale.com/util/dnsname"
"tailscale.com/version" "tailscale.com/version"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
@ -516,6 +517,21 @@ func TestGetTypeHasher(t *testing.T) {
val: time.Unix(0, 0).In(time.UTC), val: time.Unix(0, 0).In(time.UTC),
out: "\x141970-01-01T00:00:00Z", out: "\x141970-01-01T00:00:00Z",
}, },
{
name: "time_ptr", // addressable, as opposed to "time" test above
val: ptrTo(time.Unix(0, 0).In(time.UTC)),
out: "\x01\x141970-01-01T00:00:00Z",
},
{
name: "time_ptr_via_unexported",
val: testtype.NewUnexportedAddressableTime(time.Unix(0, 0).In(time.UTC)),
out: "\x01\x141970-01-01T00:00:00Z",
},
{
name: "time_ptr_via_unexported_value",
val: *testtype.NewUnexportedAddressableTime(time.Unix(0, 0).In(time.UTC)),
want: false, // neither addressable nor interface-able
},
{ {
name: "time_custom_zone", name: "time_custom_zone",
val: time.Unix(1655311822, 0).In(time.FixedZone("FOO", -60*60)), val: time.Unix(1655311822, 0).In(time.FixedZone("FOO", -60*60)),
@ -817,6 +833,26 @@ func TestArrayAllocs(t *testing.T) {
} }
} }
// Test for http://go/corp/6311 issue.
func TestHashThroughView(t *testing.T) {
type sshPolicyOut struct {
Rules []tailcfg.SSHRuleView
}
type mapResponseOut struct {
SSHPolicy *sshPolicyOut
}
// Just test we don't panic:
_ = Hash(&mapResponseOut{
SSHPolicy: &sshPolicyOut{
Rules: []tailcfg.SSHRuleView{
(&tailcfg.SSHRule{
RuleExpires: ptrTo(time.Unix(123, 0)),
}).View(),
},
},
})
}
func BenchmarkHashArray(b *testing.B) { func BenchmarkHashArray(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
type T struct { type T struct {

@ -0,0 +1,16 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package testtype contains types for testing deephash.
package testtype
import "time"
type UnexportedAddressableTime struct {
t time.Time
}
func NewUnexportedAddressableTime(t time.Time) *UnexportedAddressableTime {
return &UnexportedAddressableTime{t: t}
}
Loading…
Cancel
Save