deephash_test.go 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package deephash
  4. import (
  5. "archive/tar"
  6. "crypto/sha256"
  7. "encoding/binary"
  8. "fmt"
  9. "hash"
  10. "math"
  11. "math/bits"
  12. "math/rand"
  13. "net/netip"
  14. "reflect"
  15. "runtime"
  16. "testing"
  17. "testing/quick"
  18. "time"
  19. qt "github.com/frankban/quicktest"
  20. "go4.org/mem"
  21. "go4.org/netipx"
  22. "tailscale.com/tailcfg"
  23. "tailscale.com/types/dnstype"
  24. "tailscale.com/types/ipproto"
  25. "tailscale.com/types/key"
  26. "tailscale.com/types/ptr"
  27. "tailscale.com/types/views"
  28. "tailscale.com/util/deephash/testtype"
  29. "tailscale.com/util/dnsname"
  30. "tailscale.com/util/hashx"
  31. "tailscale.com/version"
  32. "tailscale.com/wgengine/filter"
  33. "tailscale.com/wgengine/router"
  34. "tailscale.com/wgengine/wgcfg"
  35. )
  36. type appendBytes []byte
  37. func (p appendBytes) AppendTo(b []byte) []byte {
  38. return append(b, p...)
  39. }
  40. type selfHasherValueRecv struct {
  41. emit uint64
  42. }
  43. func (s selfHasherValueRecv) Hash(h *hashx.Block512) {
  44. h.HashUint64(s.emit)
  45. }
  46. type selfHasherPointerRecv struct {
  47. emit uint64
  48. }
  49. func (s *selfHasherPointerRecv) Hash(h *hashx.Block512) {
  50. h.HashUint64(s.emit)
  51. }
  52. func TestHash(t *testing.T) {
  53. type tuple [2]any
  54. type iface struct{ X any }
  55. type scalars struct {
  56. I8 int8
  57. I16 int16
  58. I32 int32
  59. I64 int64
  60. I int
  61. U8 uint8
  62. U16 uint16
  63. U32 uint32
  64. U64 uint64
  65. U uint
  66. UP uintptr
  67. F32 float32
  68. F64 float64
  69. C64 complex64
  70. C128 complex128
  71. }
  72. type MyBool bool
  73. type MyHeader tar.Header
  74. var zeroFloat64 float64
  75. tests := []struct {
  76. in tuple
  77. wantEq bool
  78. }{
  79. {in: tuple{false, true}, wantEq: false},
  80. {in: tuple{true, true}, wantEq: true},
  81. {in: tuple{false, false}, wantEq: true},
  82. {
  83. in: tuple{
  84. scalars{-8, -16, -32, -64, -1234, 8, 16, 32, 64, 1234, 5678, 32.32, 64.64, 32 + 32i, 64 + 64i},
  85. scalars{-8, -16, -32, -64, -1234, 8, 16, 32, 64, 1234, 5678, 32.32, 64.64, 32 + 32i, 64 + 64i},
  86. },
  87. wantEq: true,
  88. },
  89. {in: tuple{scalars{I8: math.MinInt8}, scalars{I8: math.MinInt8 / 2}}, wantEq: false},
  90. {in: tuple{scalars{I16: math.MinInt16}, scalars{I16: math.MinInt16 / 2}}, wantEq: false},
  91. {in: tuple{scalars{I32: math.MinInt32}, scalars{I32: math.MinInt32 / 2}}, wantEq: false},
  92. {in: tuple{scalars{I64: math.MinInt64}, scalars{I64: math.MinInt64 / 2}}, wantEq: false},
  93. {in: tuple{scalars{I: -1234}, scalars{I: -1234 / 2}}, wantEq: false},
  94. {in: tuple{scalars{U8: math.MaxUint8}, scalars{U8: math.MaxUint8 / 2}}, wantEq: false},
  95. {in: tuple{scalars{U16: math.MaxUint16}, scalars{U16: math.MaxUint16 / 2}}, wantEq: false},
  96. {in: tuple{scalars{U32: math.MaxUint32}, scalars{U32: math.MaxUint32 / 2}}, wantEq: false},
  97. {in: tuple{scalars{U64: math.MaxUint64}, scalars{U64: math.MaxUint64 / 2}}, wantEq: false},
  98. {in: tuple{scalars{U: 1234}, scalars{U: 1234 / 2}}, wantEq: false},
  99. {in: tuple{scalars{UP: 5678}, scalars{UP: 5678 / 2}}, wantEq: false},
  100. {in: tuple{scalars{F32: 32.32}, scalars{F32: math.Nextafter32(32.32, 0)}}, wantEq: false},
  101. {in: tuple{scalars{F64: 64.64}, scalars{F64: math.Nextafter(64.64, 0)}}, wantEq: false},
  102. {in: tuple{scalars{F32: float32(math.NaN())}, scalars{F32: float32(math.NaN())}}, wantEq: true},
  103. {in: tuple{scalars{F64: float64(math.NaN())}, scalars{F64: float64(math.NaN())}}, wantEq: true},
  104. {in: tuple{scalars{C64: 32 + 32i}, scalars{C64: complex(math.Nextafter32(32, 0), 32)}}, wantEq: false},
  105. {in: tuple{scalars{C128: 64 + 64i}, scalars{C128: complex(math.Nextafter(64, 0), 64)}}, wantEq: false},
  106. {in: tuple{[]int(nil), []int(nil)}, wantEq: true},
  107. {in: tuple{[]int{}, []int(nil)}, wantEq: false},
  108. {in: tuple{[]int{}, []int{}}, wantEq: true},
  109. {in: tuple{[]string(nil), []string(nil)}, wantEq: true},
  110. {in: tuple{[]string{}, []string(nil)}, wantEq: false},
  111. {in: tuple{[]string{}, []string{}}, wantEq: true},
  112. {in: tuple{[]appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}, []appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}}, wantEq: true},
  113. {in: tuple{[]appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}, []appendBytes{{0, 0, 0, 0, 0, 0, 0, 1}, {}}}, wantEq: false},
  114. {in: tuple{iface{MyBool(true)}, iface{MyBool(true)}}, wantEq: true},
  115. {in: tuple{iface{true}, iface{MyBool(true)}}, wantEq: false},
  116. {in: tuple{iface{MyHeader{}}, iface{MyHeader{}}}, wantEq: true},
  117. {in: tuple{iface{MyHeader{}}, iface{tar.Header{}}}, wantEq: false},
  118. {in: tuple{iface{&MyHeader{}}, iface{&MyHeader{}}}, wantEq: true},
  119. {in: tuple{iface{&MyHeader{}}, iface{&tar.Header{}}}, wantEq: false},
  120. {in: tuple{iface{[]map[string]MyBool{}}, iface{[]map[string]MyBool{}}}, wantEq: true},
  121. {in: tuple{iface{[]map[string]bool{}}, iface{[]map[string]MyBool{}}}, wantEq: false},
  122. {in: tuple{zeroFloat64, -zeroFloat64}, wantEq: false}, // Issue 4883 (false alarm)
  123. {in: tuple{[]any(nil), 0.0}, wantEq: false}, // Issue 4883
  124. {in: tuple{[]any(nil), uint8(0)}, wantEq: false}, // Issue 4883
  125. {in: tuple{nil, nil}, wantEq: true}, // Issue 4883
  126. {
  127. in: func() tuple {
  128. i1 := 1
  129. i2 := 2
  130. v1 := [3]*int{&i1, &i2, &i1}
  131. v2 := [3]*int{&i1, &i2, &i2}
  132. return tuple{v1, v2}
  133. }(),
  134. wantEq: false,
  135. },
  136. {in: tuple{netip.Addr{}, netip.Addr{}}, wantEq: true},
  137. {in: tuple{netip.Addr{}, netip.AddrFrom4([4]byte{})}, wantEq: false},
  138. {in: tuple{netip.AddrFrom4([4]byte{}), netip.AddrFrom4([4]byte{})}, wantEq: true},
  139. {in: tuple{netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 1})}, wantEq: true},
  140. {in: tuple{netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 2})}, wantEq: false},
  141. {in: tuple{netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{})}, wantEq: false},
  142. {in: tuple{netip.AddrFrom16([16]byte{}), netip.AddrFrom16([16]byte{})}, wantEq: true},
  143. {in: tuple{netip.AddrPort{}, netip.AddrPort{}}, wantEq: true},
  144. {in: tuple{netip.AddrPort{}, netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 0)}, wantEq: false},
  145. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 0), netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 0)}, wantEq: true},
  146. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234), netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234)}, wantEq: true},
  147. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234), netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1235)}, wantEq: false},
  148. {in: tuple{netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1234), netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 0, 2}), 1234)}, wantEq: false},
  149. {in: tuple{netip.Prefix{}, netip.Prefix{}}, wantEq: true},
  150. {in: tuple{netip.Prefix{}, netip.PrefixFrom(netip.Addr{}, 1)}, wantEq: true},
  151. {in: tuple{netip.Prefix{}, netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 0)}, wantEq: false},
  152. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{}), 1)}, wantEq: true},
  153. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1)}, wantEq: true},
  154. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 0)}, wantEq: false},
  155. {in: tuple{netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), 1), netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 168, 0, 2}), 1)}, wantEq: false},
  156. {in: tuple{netipx.IPRange{}, netipx.IPRange{}}, wantEq: true},
  157. {in: tuple{netipx.IPRange{}, netipx.IPRangeFrom(netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{}))}, wantEq: false},
  158. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{}), netip.AddrFrom16([16]byte{}))}, wantEq: true},
  159. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100}))}, wantEq: true},
  160. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 101}))}, wantEq: false},
  161. {in: tuple{netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 1}), netip.AddrFrom4([4]byte{192, 168, 0, 100})), netipx.IPRangeFrom(netip.AddrFrom4([4]byte{192, 168, 0, 2}), netip.AddrFrom4([4]byte{192, 168, 0, 100}))}, wantEq: false},
  162. {in: tuple{key.DiscoPublic{}, key.DiscoPublic{}}, wantEq: true},
  163. {in: tuple{key.DiscoPublic{}, key.DiscoPublicFromRaw32(mem.B(func() []byte {
  164. b := make([]byte, 32)
  165. b[0] = 1
  166. return b
  167. }()))}, wantEq: false},
  168. {in: tuple{key.NodePublic{}, key.NodePublic{}}, wantEq: true},
  169. {in: tuple{key.NodePublic{}, key.NodePublicFromRaw32(mem.B(func() []byte {
  170. b := make([]byte, 32)
  171. b[0] = 1
  172. return b
  173. }()))}, wantEq: false},
  174. {in: tuple{&selfHasherPointerRecv{}, &selfHasherPointerRecv{}}, wantEq: true},
  175. {in: tuple{(*selfHasherPointerRecv)(nil), (*selfHasherPointerRecv)(nil)}, wantEq: true},
  176. {in: tuple{(*selfHasherPointerRecv)(nil), &selfHasherPointerRecv{}}, wantEq: false},
  177. {in: tuple{&selfHasherPointerRecv{emit: 1}, &selfHasherPointerRecv{emit: 2}}, wantEq: false},
  178. {in: tuple{selfHasherValueRecv{emit: 1}, selfHasherValueRecv{emit: 2}}, wantEq: false},
  179. {in: tuple{selfHasherValueRecv{emit: 2}, selfHasherValueRecv{emit: 2}}, wantEq: true},
  180. }
  181. for _, tt := range tests {
  182. gotEq := Hash(&tt.in[0]) == Hash(&tt.in[1])
  183. if gotEq != tt.wantEq {
  184. 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)
  185. }
  186. }
  187. }
  188. func TestDeepHash(t *testing.T) {
  189. // v contains the types of values we care about for our current callers.
  190. // Mostly we're just testing that we don't panic on handled types.
  191. v := getVal()
  192. hash1 := Hash(v)
  193. t.Logf("hash: %v", hash1)
  194. for range 20 {
  195. v := getVal()
  196. hash2 := Hash(v)
  197. if hash1 != hash2 {
  198. t.Error("second hash didn't match")
  199. }
  200. }
  201. }
  202. // Tests that we actually hash map elements. Whoops.
  203. func TestIssue4868(t *testing.T) {
  204. m1 := map[int]string{1: "foo"}
  205. m2 := map[int]string{1: "bar"}
  206. if Hash(&m1) == Hash(&m2) {
  207. t.Error("bogus")
  208. }
  209. }
  210. func TestIssue4871(t *testing.T) {
  211. m1 := map[string]string{"": "", "x": "foo"}
  212. m2 := map[string]string{}
  213. if h1, h2 := Hash(&m1), Hash(&m2); h1 == h2 {
  214. t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
  215. }
  216. }
  217. func TestNilVsEmptymap(t *testing.T) {
  218. m1 := map[string]string(nil)
  219. m2 := map[string]string{}
  220. if h1, h2 := Hash(&m1), Hash(&m2); h1 == h2 {
  221. t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
  222. }
  223. }
  224. func TestMapFraming(t *testing.T) {
  225. m1 := map[string]string{"foo": "", "fo": "o"}
  226. m2 := map[string]string{}
  227. if h1, h2 := Hash(&m1), Hash(&m2); h1 == h2 {
  228. t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
  229. }
  230. }
  231. func TestQuick(t *testing.T) {
  232. initSeed()
  233. err := quick.Check(func(v, w map[string]string) bool {
  234. return (Hash(&v) == Hash(&w)) == reflect.DeepEqual(v, w)
  235. }, &quick.Config{MaxCount: 1000, Rand: rand.New(rand.NewSource(int64(seed)))})
  236. if err != nil {
  237. t.Fatalf("seed=%v, err=%v", seed, err)
  238. }
  239. }
  240. type tailscaleTypes struct {
  241. WGConfig *wgcfg.Config
  242. RouterConfig *router.Config
  243. MapFQDNAddrs map[dnsname.FQDN][]netip.Addr
  244. MapFQDNAddrPorts map[dnsname.FQDN][]netip.AddrPort
  245. MapDiscoPublics map[key.DiscoPublic]bool
  246. MapResponse *tailcfg.MapResponse
  247. FilterMatch filter.Match
  248. }
  249. func getVal() *tailscaleTypes {
  250. return &tailscaleTypes{
  251. &wgcfg.Config{
  252. Name: "foo",
  253. Addresses: []netip.Prefix{netip.PrefixFrom(netip.AddrFrom16([16]byte{3: 3}).Unmap(), 5)},
  254. Peers: []wgcfg.Peer{
  255. {
  256. PublicKey: key.NodePublic{},
  257. },
  258. },
  259. },
  260. &router.Config{
  261. Routes: []netip.Prefix{
  262. netip.MustParsePrefix("1.2.3.0/24"),
  263. netip.MustParsePrefix("1234::/64"),
  264. },
  265. },
  266. map[dnsname.FQDN][]netip.Addr{
  267. dnsname.FQDN("a."): {netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("4.3.2.1")},
  268. dnsname.FQDN("b."): {netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("9.9.9.9")},
  269. dnsname.FQDN("c."): {netip.MustParseAddr("6.6.6.6"), netip.MustParseAddr("7.7.7.7")},
  270. dnsname.FQDN("d."): {netip.MustParseAddr("6.7.6.6"), netip.MustParseAddr("7.7.7.8")},
  271. dnsname.FQDN("e."): {netip.MustParseAddr("6.8.6.6"), netip.MustParseAddr("7.7.7.9")},
  272. dnsname.FQDN("f."): {netip.MustParseAddr("6.9.6.6"), netip.MustParseAddr("7.7.7.0")},
  273. },
  274. map[dnsname.FQDN][]netip.AddrPort{
  275. dnsname.FQDN("a."): {netip.MustParseAddrPort("1.2.3.4:11"), netip.MustParseAddrPort("4.3.2.1:22")},
  276. dnsname.FQDN("b."): {netip.MustParseAddrPort("8.8.8.8:11"), netip.MustParseAddrPort("9.9.9.9:22")},
  277. dnsname.FQDN("c."): {netip.MustParseAddrPort("8.8.8.8:12"), netip.MustParseAddrPort("9.9.9.9:23")},
  278. dnsname.FQDN("d."): {netip.MustParseAddrPort("8.8.8.8:13"), netip.MustParseAddrPort("9.9.9.9:24")},
  279. dnsname.FQDN("e."): {netip.MustParseAddrPort("8.8.8.8:14"), netip.MustParseAddrPort("9.9.9.9:25")},
  280. },
  281. map[key.DiscoPublic]bool{
  282. key.DiscoPublicFromRaw32(mem.B([]byte{1: 1, 31: 0})): true,
  283. key.DiscoPublicFromRaw32(mem.B([]byte{1: 2, 31: 0})): false,
  284. key.DiscoPublicFromRaw32(mem.B([]byte{1: 3, 31: 0})): true,
  285. key.DiscoPublicFromRaw32(mem.B([]byte{1: 4, 31: 0})): false,
  286. },
  287. &tailcfg.MapResponse{
  288. DERPMap: &tailcfg.DERPMap{
  289. Regions: map[int]*tailcfg.DERPRegion{
  290. 1: {
  291. RegionID: 1,
  292. RegionCode: "foo",
  293. Nodes: []*tailcfg.DERPNode{
  294. {
  295. Name: "n1",
  296. RegionID: 1,
  297. HostName: "foo.com",
  298. },
  299. {
  300. Name: "n2",
  301. RegionID: 1,
  302. HostName: "bar.com",
  303. },
  304. },
  305. },
  306. },
  307. },
  308. DNSConfig: &tailcfg.DNSConfig{
  309. Resolvers: []*dnstype.Resolver{
  310. {Addr: "10.0.0.1"},
  311. },
  312. },
  313. PacketFilter: []tailcfg.FilterRule{
  314. {
  315. SrcIPs: []string{"1.2.3.4"},
  316. DstPorts: []tailcfg.NetPortRange{
  317. {
  318. IP: "1.2.3.4/32",
  319. Ports: tailcfg.PortRange{First: 1, Last: 2},
  320. },
  321. },
  322. },
  323. },
  324. Peers: []*tailcfg.Node{
  325. {
  326. ID: 1,
  327. },
  328. {
  329. ID: 2,
  330. },
  331. },
  332. UserProfiles: []tailcfg.UserProfile{
  333. {ID: 1, LoginName: "[email protected]"},
  334. {ID: 2, LoginName: "[email protected]"},
  335. },
  336. },
  337. filter.Match{
  338. IPProto: views.SliceOf([]ipproto.Proto{1, 2, 3}),
  339. },
  340. }
  341. }
  342. type IntThenByte struct {
  343. _ int
  344. _ byte
  345. }
  346. type TwoInts struct{ _, _ int }
  347. type IntIntByteInt struct {
  348. i1, i2 int32
  349. b byte // padding after
  350. i3 int32
  351. }
  352. func u8(n uint8) string { return string([]byte{n}) }
  353. func u32(n uint32) string { return string(binary.LittleEndian.AppendUint32(nil, n)) }
  354. func u64(n uint64) string { return string(binary.LittleEndian.AppendUint64(nil, n)) }
  355. func ux(n uint) string {
  356. if bits.UintSize == 32 {
  357. return u32(uint32(n))
  358. } else {
  359. return u64(uint64(n))
  360. }
  361. }
  362. func TestGetTypeHasher(t *testing.T) {
  363. switch runtime.GOARCH {
  364. case "amd64", "arm64", "arm", "386", "riscv64":
  365. default:
  366. // Test outputs below are specifically for little-endian machines.
  367. // Just skip everything else for now. Feel free to add more above if
  368. // you have the hardware to test and it's little-endian.
  369. t.Skipf("skipping on %v", runtime.GOARCH)
  370. }
  371. type typedString string
  372. var (
  373. someInt = int('A')
  374. someComplex128 = complex128(1 + 2i)
  375. someIP = netip.MustParseAddr("1.2.3.4")
  376. )
  377. tests := []struct {
  378. name string
  379. val any
  380. out string
  381. out32 string // overwrites out if 32-bit
  382. }{
  383. {
  384. name: "int",
  385. val: int(1),
  386. out: ux(1),
  387. },
  388. {
  389. name: "int_negative",
  390. val: int(-1),
  391. out: ux(math.MaxUint),
  392. },
  393. {
  394. name: "int8",
  395. val: int8(1),
  396. out: "\x01",
  397. },
  398. {
  399. name: "float64",
  400. val: float64(1.0),
  401. out: "\x00\x00\x00\x00\x00\x00\xf0?",
  402. },
  403. {
  404. name: "float32",
  405. val: float32(1.0),
  406. out: "\x00\x00\x80?",
  407. },
  408. {
  409. name: "string",
  410. val: "foo",
  411. out: "\x03\x00\x00\x00\x00\x00\x00\x00foo",
  412. },
  413. {
  414. name: "typedString",
  415. val: typedString("foo"),
  416. out: "\x03\x00\x00\x00\x00\x00\x00\x00foo",
  417. },
  418. {
  419. name: "string_slice",
  420. val: []string{"foo", "bar"},
  421. out: "\x01\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x03\x00\x00\x00\x00\x00\x00\x00bar",
  422. },
  423. {
  424. name: "int_slice",
  425. val: []int{1, 0, -1},
  426. out: "\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff",
  427. out32: "\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff",
  428. },
  429. {
  430. name: "struct",
  431. val: struct {
  432. a, b int
  433. c uint16
  434. }{1, -1, 2},
  435. out: "\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x02\x00",
  436. out32: "\x01\x00\x00\x00\xff\xff\xff\xff\x02\x00",
  437. },
  438. {
  439. name: "nil_int_ptr",
  440. val: (*int)(nil),
  441. out: "\x00",
  442. },
  443. {
  444. name: "int_ptr",
  445. val: &someInt,
  446. out: "\x01A\x00\x00\x00\x00\x00\x00\x00",
  447. out32: "\x01A\x00\x00\x00",
  448. },
  449. {
  450. name: "nil_uint32_ptr",
  451. val: (*uint32)(nil),
  452. out: "\x00",
  453. },
  454. {
  455. name: "complex128_ptr",
  456. val: &someComplex128,
  457. out: "\x01\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@",
  458. },
  459. {
  460. name: "packet_filter",
  461. val: filterRules,
  462. out: "\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04!\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00",
  463. out32: "\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x01\x00\x02\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04!\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00",
  464. },
  465. {
  466. name: "netip.Addr",
  467. val: netip.MustParseAddr("fe80::123%foo"),
  468. out: u64(16+3) + u64(0x80fe) + u64(0x2301<<48) + "foo",
  469. },
  470. {
  471. name: "ptr-netip.Addr",
  472. val: &someIP,
  473. out: u8(1) + u64(4) + u32(0x04030201),
  474. },
  475. {
  476. name: "ptr-nil-netip.Addr",
  477. val: (*netip.Addr)(nil),
  478. out: "\x00",
  479. },
  480. {
  481. name: "time",
  482. val: time.Unix(1234, 5678).In(time.UTC),
  483. out: u64(1234) + u32(5678) + u32(0),
  484. },
  485. {
  486. name: "time_ptr", // addressable, as opposed to "time" test above
  487. val: ptr.To(time.Unix(1234, 5678).In(time.UTC)),
  488. out: u8(1) + u64(1234) + u32(5678) + u32(0),
  489. },
  490. {
  491. name: "time_ptr_via_unexported",
  492. val: testtype.NewUnexportedAddressableTime(time.Unix(1234, 5678).In(time.UTC)),
  493. out: u8(1) + u64(1234) + u32(5678) + u32(0),
  494. },
  495. {
  496. name: "time_ptr_via_unexported_value",
  497. val: *testtype.NewUnexportedAddressableTime(time.Unix(1234, 5678).In(time.UTC)),
  498. out: u64(1234) + u32(5678) + u32(0),
  499. },
  500. {
  501. name: "time_custom_zone",
  502. val: time.Unix(1655311822, 0).In(time.FixedZone("FOO", -60*60)),
  503. out: u64(1655311822) + u32(0) + u32(math.MaxUint32-60*60+1),
  504. },
  505. {
  506. name: "time_nil",
  507. val: (*time.Time)(nil),
  508. out: "\x00",
  509. },
  510. {
  511. name: "array_memhash",
  512. val: [4]byte{1, 2, 3, 4},
  513. out: "\x01\x02\x03\x04",
  514. },
  515. {
  516. name: "array_ptr_memhash",
  517. val: ptr.To([4]byte{1, 2, 3, 4}),
  518. out: "\x01\x01\x02\x03\x04",
  519. },
  520. {
  521. name: "ptr_to_struct_partially_memhashable",
  522. val: &struct {
  523. A int16
  524. B int16
  525. C *int
  526. }{5, 6, nil},
  527. out: "\x01\x05\x00\x06\x00\x00",
  528. },
  529. {
  530. name: "struct_partially_memhashable_but_cant_addr",
  531. val: struct {
  532. A int16
  533. B int16
  534. C *int
  535. }{5, 6, nil},
  536. out: "\x05\x00\x06\x00\x00",
  537. },
  538. {
  539. name: "array_elements",
  540. val: [4]byte{1, 2, 3, 4},
  541. out: "\x01\x02\x03\x04",
  542. },
  543. {
  544. name: "bool",
  545. val: true,
  546. out: "\x01",
  547. },
  548. {
  549. name: "IntIntByteInt",
  550. val: IntIntByteInt{1, 2, 3, 4},
  551. out: "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00",
  552. },
  553. {
  554. name: "IntIntByteInt-canaddr",
  555. val: &IntIntByteInt{1, 2, 3, 4},
  556. out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00",
  557. },
  558. {
  559. name: "array-IntIntByteInt",
  560. val: [2]IntIntByteInt{
  561. {1, 2, 3, 4},
  562. {5, 6, 7, 8},
  563. },
  564. out: "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00",
  565. },
  566. {
  567. name: "array-IntIntByteInt-canaddr",
  568. val: &[2]IntIntByteInt{
  569. {1, 2, 3, 4},
  570. {5, 6, 7, 8},
  571. },
  572. out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00",
  573. },
  574. {
  575. name: "tailcfg.Node",
  576. val: &tailcfg.Node{},
  577. out: "ANY", // magic value; just check it doesn't fail to hash
  578. out32: "ANY",
  579. },
  580. }
  581. for _, tt := range tests {
  582. t.Run(tt.name, func(t *testing.T) {
  583. rv := reflect.ValueOf(tt.val)
  584. va := reflect.New(rv.Type()).Elem()
  585. va.Set(rv)
  586. fn := lookupTypeHasher(va.Type())
  587. hb := &hashBuffer{Hash: sha256.New()}
  588. h := new(hasher)
  589. h.Block512.Hash = hb
  590. fn(h, pointerOf(va.Addr()))
  591. const ptrSize = 32 << uintptr(^uintptr(0)>>63)
  592. if tt.out32 != "" && ptrSize == 32 {
  593. tt.out = tt.out32
  594. }
  595. h.sum()
  596. if got := string(hb.B); got != tt.out && tt.out != "ANY" {
  597. t.Fatalf("got %q; want %q", got, tt.out)
  598. }
  599. })
  600. }
  601. }
  602. func TestSliceCycle(t *testing.T) {
  603. type S []S
  604. c := qt.New(t)
  605. a := make(S, 1) // cyclic graph of 1 node
  606. a[0] = a
  607. b := make(S, 1) // cyclic graph of 1 node
  608. b[0] = b
  609. ha := Hash(&a)
  610. hb := Hash(&b)
  611. c.Assert(ha, qt.Equals, hb)
  612. c1 := make(S, 1) // cyclic graph of 2 nodes
  613. c2 := make(S, 1) // cyclic graph of 2 nodes
  614. c1[0] = c2
  615. c2[0] = c1
  616. hc1 := Hash(&c1)
  617. hc2 := Hash(&c2)
  618. c.Assert(hc1, qt.Equals, hc2)
  619. c.Assert(ha, qt.Not(qt.Equals), hc1)
  620. c.Assert(hb, qt.Not(qt.Equals), hc2)
  621. c3 := make(S, 1) // graph of 1 node pointing to cyclic graph of 2 nodes
  622. c3[0] = c1
  623. hc3 := Hash(&c3)
  624. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  625. c4 := make(S, 2) // cyclic graph of 3 nodes
  626. c5 := make(S, 2) // cyclic graph of 3 nodes
  627. c4[0] = nil
  628. c4[1] = c4
  629. c5[0] = c5
  630. c5[1] = nil
  631. hc4 := Hash(&c4)
  632. hc5 := Hash(&c5)
  633. c.Assert(hc4, qt.Not(qt.Equals), hc5) // cycle occurs through different indexes
  634. }
  635. func TestMapCycle(t *testing.T) {
  636. type M map[string]M
  637. c := qt.New(t)
  638. a := make(M) // cyclic graph of 1 node
  639. a["self"] = a
  640. b := make(M) // cyclic graph of 1 node
  641. b["self"] = b
  642. ha := Hash(&a)
  643. hb := Hash(&b)
  644. c.Assert(ha, qt.Equals, hb)
  645. c1 := make(M) // cyclic graph of 2 nodes
  646. c2 := make(M) // cyclic graph of 2 nodes
  647. c1["peer"] = c2
  648. c2["peer"] = c1
  649. hc1 := Hash(&c1)
  650. hc2 := Hash(&c2)
  651. c.Assert(hc1, qt.Equals, hc2)
  652. c.Assert(ha, qt.Not(qt.Equals), hc1)
  653. c.Assert(hb, qt.Not(qt.Equals), hc2)
  654. c3 := make(M) // graph of 1 node pointing to cyclic graph of 2 nodes
  655. c3["child"] = c1
  656. hc3 := Hash(&c3)
  657. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  658. c4 := make(M) // cyclic graph of 3 nodes
  659. c5 := make(M) // cyclic graph of 3 nodes
  660. c4["0"] = nil
  661. c4["1"] = c4
  662. c5["0"] = c5
  663. c5["1"] = nil
  664. hc4 := Hash(&c4)
  665. hc5 := Hash(&c5)
  666. c.Assert(hc4, qt.Not(qt.Equals), hc5) // cycle occurs through different keys
  667. }
  668. func TestPointerCycle(t *testing.T) {
  669. type P *P
  670. c := qt.New(t)
  671. a := new(P) // cyclic graph of 1 node
  672. *a = a
  673. b := new(P) // cyclic graph of 1 node
  674. *b = b
  675. ha := Hash(&a)
  676. hb := Hash(&b)
  677. c.Assert(ha, qt.Equals, hb)
  678. c1 := new(P) // cyclic graph of 2 nodes
  679. c2 := new(P) // cyclic graph of 2 nodes
  680. *c1 = c2
  681. *c2 = c1
  682. hc1 := Hash(&c1)
  683. hc2 := Hash(&c2)
  684. c.Assert(hc1, qt.Equals, hc2)
  685. c.Assert(ha, qt.Not(qt.Equals), hc1)
  686. c.Assert(hb, qt.Not(qt.Equals), hc2)
  687. c3 := new(P) // graph of 1 node pointing to cyclic graph of 2 nodes
  688. *c3 = c1
  689. hc3 := Hash(&c3)
  690. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  691. }
  692. func TestInterfaceCycle(t *testing.T) {
  693. type I struct{ v any }
  694. c := qt.New(t)
  695. a := new(I) // cyclic graph of 1 node
  696. a.v = a
  697. b := new(I) // cyclic graph of 1 node
  698. b.v = b
  699. ha := Hash(&a)
  700. hb := Hash(&b)
  701. c.Assert(ha, qt.Equals, hb)
  702. c1 := new(I) // cyclic graph of 2 nodes
  703. c2 := new(I) // cyclic graph of 2 nodes
  704. c1.v = c2
  705. c2.v = c1
  706. hc1 := Hash(&c1)
  707. hc2 := Hash(&c2)
  708. c.Assert(hc1, qt.Equals, hc2)
  709. c.Assert(ha, qt.Not(qt.Equals), hc1)
  710. c.Assert(hb, qt.Not(qt.Equals), hc2)
  711. c3 := new(I) // graph of 1 node pointing to cyclic graph of 2 nodes
  712. c3.v = c1
  713. hc3 := Hash(&c3)
  714. c.Assert(hc1, qt.Not(qt.Equals), hc3)
  715. }
  716. var sink Sum
  717. func BenchmarkHash(b *testing.B) {
  718. b.ReportAllocs()
  719. v := getVal()
  720. for range b.N {
  721. sink = Hash(v)
  722. }
  723. }
  724. // filterRules is a packet filter that has both everything populated (in its
  725. // first element) and also a few entries that are the typical shape for regular
  726. // packet filters as sent to clients.
  727. var filterRules = []tailcfg.FilterRule{
  728. {
  729. SrcIPs: []string{"*", "10.1.3.4/32", "10.0.0.0/24"},
  730. DstPorts: []tailcfg.NetPortRange{{
  731. IP: "1.2.3.4/32",
  732. Bits: ptr.To(32),
  733. Ports: tailcfg.PortRange{First: 1, Last: 2},
  734. }},
  735. IPProto: []int{1, 2, 3, 4},
  736. CapGrant: []tailcfg.CapGrant{{
  737. Dsts: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")},
  738. Caps: []tailcfg.PeerCapability{"foo"},
  739. }},
  740. },
  741. {
  742. SrcIPs: []string{"foooooooooo"},
  743. DstPorts: []tailcfg.NetPortRange{{
  744. IP: "baaaaaarrrrr",
  745. Ports: tailcfg.PortRange{First: 1, Last: 2},
  746. }},
  747. },
  748. {
  749. SrcIPs: []string{"foooooooooo"},
  750. DstPorts: []tailcfg.NetPortRange{{
  751. IP: "baaaaaarrrrr",
  752. Ports: tailcfg.PortRange{First: 1, Last: 2},
  753. }},
  754. },
  755. {
  756. SrcIPs: []string{"foooooooooo"},
  757. DstPorts: []tailcfg.NetPortRange{{
  758. IP: "baaaaaarrrrr",
  759. Ports: tailcfg.PortRange{First: 1, Last: 2},
  760. }},
  761. },
  762. }
  763. func BenchmarkHashPacketFilter(b *testing.B) {
  764. b.ReportAllocs()
  765. for range b.N {
  766. sink = Hash(&filterRules)
  767. }
  768. }
  769. func TestHashMapAcyclic(t *testing.T) {
  770. m := map[int]string{}
  771. for i := range 100 {
  772. m[i] = fmt.Sprint(i)
  773. }
  774. got := map[string]bool{}
  775. hb := &hashBuffer{Hash: sha256.New()}
  776. hash := lookupTypeHasher(reflect.TypeFor[map[int]string]())
  777. for range 20 {
  778. va := reflect.ValueOf(&m).Elem()
  779. hb.Reset()
  780. h := new(hasher)
  781. h.Block512.Hash = hb
  782. hash(h, pointerOf(va.Addr()))
  783. h.sum()
  784. if got[string(hb.B)] {
  785. continue
  786. }
  787. got[string(hb.B)] = true
  788. }
  789. if len(got) != 1 {
  790. t.Errorf("got %d results; want 1", len(got))
  791. }
  792. }
  793. func TestPrintArray(t *testing.T) {
  794. type T struct {
  795. X [32]byte
  796. }
  797. x := T{X: [32]byte{1: 1, 31: 31}}
  798. hb := &hashBuffer{Hash: sha256.New()}
  799. h := new(hasher)
  800. h.Block512.Hash = hb
  801. va := reflect.ValueOf(&x).Elem()
  802. hash := lookupTypeHasher(va.Type())
  803. hash(h, pointerOf(va.Addr()))
  804. h.sum()
  805. const want = "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f"
  806. if got := hb.B; string(got) != want {
  807. t.Errorf("wrong:\n got: %q\nwant: %q\n", got, want)
  808. }
  809. }
  810. func BenchmarkHashMapAcyclic(b *testing.B) {
  811. b.ReportAllocs()
  812. m := map[int]string{}
  813. for i := range 100 {
  814. m[i] = fmt.Sprint(i)
  815. }
  816. hb := &hashBuffer{Hash: sha256.New()}
  817. va := reflect.ValueOf(&m).Elem()
  818. hash := lookupTypeHasher(va.Type())
  819. h := new(hasher)
  820. h.Block512.Hash = hb
  821. for range b.N {
  822. h.Reset()
  823. hash(h, pointerOf(va.Addr()))
  824. }
  825. }
  826. func BenchmarkTailcfgNode(b *testing.B) {
  827. b.ReportAllocs()
  828. node := new(tailcfg.Node)
  829. for range b.N {
  830. sink = Hash(node)
  831. }
  832. }
  833. func TestExhaustive(t *testing.T) {
  834. seen := make(map[Sum]bool)
  835. for i := range 100000 {
  836. s := Hash(&i)
  837. if seen[s] {
  838. t.Fatalf("hash collision %v", i)
  839. }
  840. seen[s] = true
  841. }
  842. }
  843. // verify this doesn't loop forever, as it used to (Issue 2340)
  844. func TestMapCyclicFallback(t *testing.T) {
  845. type T struct {
  846. M map[string]any
  847. }
  848. v := &T{
  849. M: map[string]any{},
  850. }
  851. v.M["m"] = v.M
  852. Hash(v)
  853. }
  854. func TestArrayAllocs(t *testing.T) {
  855. if version.IsRace() {
  856. t.Skip("skipping test under race detector")
  857. }
  858. // In theory, there should be no allocations. However, escape analysis on
  859. // certain architectures fails to detect that certain cases do not escape.
  860. // This discrepancy currently affects sha256.digest.Sum.
  861. // Measure the number of allocations in sha256 to ensure that Hash does
  862. // not allocate on top of its usage of sha256.
  863. // See https://golang.org/issue/48055.
  864. var b []byte
  865. h := sha256.New()
  866. want := int(testing.AllocsPerRun(1000, func() {
  867. b = h.Sum(b[:0])
  868. }))
  869. switch runtime.GOARCH {
  870. case "amd64", "arm64":
  871. want = 0 // ensure no allocations on popular architectures
  872. }
  873. type T struct {
  874. X [32]byte
  875. }
  876. x := &T{X: [32]byte{1: 1, 2: 2, 3: 3, 4: 4}}
  877. got := int(testing.AllocsPerRun(1000, func() {
  878. sink = Hash(x)
  879. }))
  880. if got > want {
  881. t.Errorf("allocs = %v; want %v", got, want)
  882. }
  883. }
  884. // Test for http://go/corp/6311 issue.
  885. func TestHashThroughView(t *testing.T) {
  886. type sshPolicyOut struct {
  887. Rules []tailcfg.SSHRuleView
  888. }
  889. type mapResponseOut struct {
  890. SSHPolicy *sshPolicyOut
  891. }
  892. // Just test we don't panic:
  893. _ = Hash(&mapResponseOut{
  894. SSHPolicy: &sshPolicyOut{
  895. Rules: []tailcfg.SSHRuleView{
  896. (&tailcfg.SSHRule{
  897. RuleExpires: ptr.To(time.Unix(123, 0)),
  898. }).View(),
  899. },
  900. },
  901. })
  902. }
  903. func BenchmarkHashArray(b *testing.B) {
  904. b.ReportAllocs()
  905. type T struct {
  906. X [32]byte
  907. }
  908. x := &T{X: [32]byte{1: 1, 2: 2, 3: 3, 4: 4}}
  909. for range b.N {
  910. sink = Hash(x)
  911. }
  912. }
  913. // hashBuffer is a hash.Hash that buffers all written data.
  914. type hashBuffer struct {
  915. hash.Hash
  916. B []byte
  917. }
  918. func (h *hashBuffer) Write(b []byte) (int, error) {
  919. n, err := h.Hash.Write(b)
  920. h.B = append(h.B, b[:n]...)
  921. return n, err
  922. }
  923. func (h *hashBuffer) Reset() {
  924. h.Hash.Reset()
  925. h.B = h.B[:0]
  926. }
  927. func FuzzTime(f *testing.F) {
  928. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), false, "", 0)
  929. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "", 0)
  930. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "hello", 0)
  931. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "", 1234)
  932. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(0), true, "hello", 1234)
  933. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), false, "", 0)
  934. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "", 0)
  935. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "hello", 0)
  936. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "", 1234)
  937. f.Add(int64(0), int64(0), false, "", 0, int64(0), int64(1), true, "hello", 1234)
  938. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0)
  939. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "", 0)
  940. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "hello", 0)
  941. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "", 1234)
  942. f.Add(int64(math.MaxInt64), int64(math.MaxInt64), false, "", 0, int64(math.MaxInt64), int64(math.MaxInt64), true, "hello", 1234)
  943. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), false, "", 0)
  944. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "", 0)
  945. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "hello", 0)
  946. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "", 1234)
  947. f.Add(int64(math.MinInt64), int64(math.MinInt64), false, "", 0, int64(math.MinInt64), int64(math.MinInt64), true, "hello", 1234)
  948. f.Fuzz(func(t *testing.T,
  949. s1, ns1 int64, loc1 bool, name1 string, off1 int,
  950. s2, ns2 int64, loc2 bool, name2 string, off2 int,
  951. ) {
  952. t1 := time.Unix(s1, ns1)
  953. if loc1 {
  954. _ = t1.In(time.FixedZone(name1, off1))
  955. }
  956. t2 := time.Unix(s2, ns2)
  957. if loc2 {
  958. _ = t2.In(time.FixedZone(name2, off2))
  959. }
  960. got := Hash(&t1) == Hash(&t2)
  961. want := t1.Format(time.RFC3339Nano) == t2.Format(time.RFC3339Nano)
  962. if got != want {
  963. t.Errorf("time.Time(%s) == time.Time(%s) mismatches hash equivalent", t1.Format(time.RFC3339Nano), t2.Format(time.RFC3339Nano))
  964. }
  965. })
  966. }
  967. func FuzzAddr(f *testing.F) {
  968. f.Fuzz(func(t *testing.T,
  969. u1a, u1b uint64, zone1 string,
  970. u2a, u2b uint64, zone2 string,
  971. ) {
  972. var b1, b2 [16]byte
  973. binary.LittleEndian.PutUint64(b1[:8], u1a)
  974. binary.LittleEndian.PutUint64(b1[8:], u1b)
  975. binary.LittleEndian.PutUint64(b2[:8], u2a)
  976. binary.LittleEndian.PutUint64(b2[8:], u2b)
  977. var ips [4]netip.Addr
  978. ips[0] = netip.AddrFrom4(*(*[4]byte)(b1[:]))
  979. ips[1] = netip.AddrFrom4(*(*[4]byte)(b2[:]))
  980. ips[2] = netip.AddrFrom16(b1)
  981. if zone1 != "" {
  982. ips[2] = ips[2].WithZone(zone1)
  983. }
  984. ips[3] = netip.AddrFrom16(b2)
  985. if zone2 != "" {
  986. ips[3] = ips[2].WithZone(zone2)
  987. }
  988. for _, ip1 := range ips[:] {
  989. for _, ip2 := range ips[:] {
  990. got := Hash(&ip1) == Hash(&ip2)
  991. want := ip1 == ip2
  992. if got != want {
  993. t.Errorf("netip.Addr(%s) == netip.Addr(%s) mismatches hash equivalent", ip1.String(), ip2.String())
  994. }
  995. }
  996. }
  997. })
  998. }
  999. func TestAppendTo(t *testing.T) {
  1000. v := getVal()
  1001. h := Hash(v)
  1002. sum := h.AppendTo(nil)
  1003. if s := h.String(); s != string(sum) {
  1004. t.Errorf("hash sum mismatch; h.String()=%q h.AppendTo()=%q", s, string(sum))
  1005. }
  1006. }
  1007. func TestFilterFields(t *testing.T) {
  1008. type T struct {
  1009. A int
  1010. B int
  1011. C int
  1012. }
  1013. hashers := map[string]func(*T) Sum{
  1014. "all": HasherForType[T](),
  1015. "ac": HasherForType[T](IncludeFields[T]("A", "C")),
  1016. "b": HasherForType[T](ExcludeFields[T]("A", "C")),
  1017. }
  1018. tests := []struct {
  1019. hasher string
  1020. a, b T
  1021. wantEq bool
  1022. }{
  1023. {"all", T{1, 2, 3}, T{1, 2, 3}, true},
  1024. {"all", T{1, 2, 3}, T{0, 2, 3}, false},
  1025. {"all", T{1, 2, 3}, T{1, 0, 3}, false},
  1026. {"all", T{1, 2, 3}, T{1, 2, 0}, false},
  1027. {"ac", T{0, 0, 0}, T{0, 0, 0}, true},
  1028. {"ac", T{1, 0, 1}, T{1, 1, 1}, true},
  1029. {"ac", T{1, 1, 1}, T{1, 1, 0}, false},
  1030. {"b", T{0, 0, 0}, T{0, 0, 0}, true},
  1031. {"b", T{1, 0, 1}, T{1, 1, 1}, false},
  1032. {"b", T{1, 1, 1}, T{0, 1, 0}, true},
  1033. }
  1034. for _, tt := range tests {
  1035. f, ok := hashers[tt.hasher]
  1036. if !ok {
  1037. t.Fatalf("bad test: unknown hasher %q", tt.hasher)
  1038. }
  1039. sum1 := f(&tt.a)
  1040. sum2 := f(&tt.b)
  1041. got := sum1 == sum2
  1042. if got != tt.wantEq {
  1043. t.Errorf("hasher %q, for %+v and %v, got equal = %v; want %v", tt.hasher, tt.a, tt.b, got, tt.wantEq)
  1044. }
  1045. }
  1046. }
  1047. func BenchmarkAppendTo(b *testing.B) {
  1048. b.ReportAllocs()
  1049. v := getVal()
  1050. h := Hash(v)
  1051. hashBuf := make([]byte, 0, 100)
  1052. b.ResetTimer()
  1053. for range b.N {
  1054. hashBuf = h.AppendTo(hashBuf[:0])
  1055. }
  1056. }