From 04cf46a762442dfc430f1706141f693341052c03 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 27 Jul 2022 09:10:22 -0700 Subject: [PATCH] util/deephash: fix unexported time.Time hashing Updates tailscale/corp#6311 Change-Id: I33cd7e4040966261c2f2eb3d32f29936aeb7f632 Signed-off-by: Brad Fitzpatrick --- util/deephash/deephash.go | 6 +++-- util/deephash/deephash_test.go | 36 ++++++++++++++++++++++++++++++ util/deephash/testtype/testtype.go | 16 +++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 util/deephash/testtype/testtype.go diff --git a/util/deephash/deephash.go b/util/deephash/deephash.go index 82bb1a05e..93ab1abc5 100644 --- a/util/deephash/deephash.go +++ b/util/deephash/deephash.go @@ -544,9 +544,11 @@ func (h *hasher) hashComplex128v(v reflect.Value) bool { func (h *hasher) hashTimev(v reflect.Value) bool { var t time.Time if v.CanAddr() { - t = *(v.Addr().Interface().(*time.Time)) - } else { + t = *(*time.Time)(v.Addr().UnsafePointer()) + } else if v.CanInterface() { t = v.Interface().(time.Time) + } else { + return false } b := t.AppendFormat(h.scratch[:1], time.RFC3339Nano) b[0] = byte(len(b) - 1) // more than sufficient width; if not, good enough. diff --git a/util/deephash/deephash_test.go b/util/deephash/deephash_test.go index e7897d0cf..3f20b8e61 100644 --- a/util/deephash/deephash_test.go +++ b/util/deephash/deephash_test.go @@ -28,6 +28,7 @@ import ( "tailscale.com/types/ipproto" "tailscale.com/types/key" "tailscale.com/types/structs" + "tailscale.com/util/deephash/testtype" "tailscale.com/util/dnsname" "tailscale.com/version" "tailscale.com/wgengine/filter" @@ -516,6 +517,21 @@ func TestGetTypeHasher(t *testing.T) { val: time.Unix(0, 0).In(time.UTC), 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", 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) { b.ReportAllocs() type T struct { diff --git a/util/deephash/testtype/testtype.go b/util/deephash/testtype/testtype.go new file mode 100644 index 000000000..c95f819f4 --- /dev/null +++ b/util/deephash/testtype/testtype.go @@ -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} +}