deephash_test.go 35 KB

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