deephash_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. // Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package deephash
  5. import (
  6. "archive/tar"
  7. "bufio"
  8. "bytes"
  9. "crypto/sha256"
  10. "fmt"
  11. "math"
  12. "reflect"
  13. "runtime"
  14. "testing"
  15. "go4.org/mem"
  16. "inet.af/netaddr"
  17. "tailscale.com/tailcfg"
  18. "tailscale.com/types/dnstype"
  19. "tailscale.com/types/ipproto"
  20. "tailscale.com/types/key"
  21. "tailscale.com/util/dnsname"
  22. "tailscale.com/version"
  23. "tailscale.com/wgengine/filter"
  24. "tailscale.com/wgengine/router"
  25. "tailscale.com/wgengine/wgcfg"
  26. )
  27. type appendBytes []byte
  28. func (p appendBytes) AppendTo(b []byte) []byte {
  29. return append(b, p...)
  30. }
  31. func TestHash(t *testing.T) {
  32. type tuple [2]interface{}
  33. type iface struct{ X interface{} }
  34. type scalars struct {
  35. I8 int8
  36. I16 int16
  37. I32 int32
  38. I64 int64
  39. I int
  40. U8 uint8
  41. U16 uint16
  42. U32 uint32
  43. U64 uint64
  44. U uint
  45. UP uintptr
  46. F32 float32
  47. F64 float64
  48. C64 complex64
  49. C128 complex128
  50. }
  51. type MyBool bool
  52. type MyHeader tar.Header
  53. tests := []struct {
  54. in tuple
  55. wantEq bool
  56. }{
  57. {in: tuple{false, true}, wantEq: false},
  58. {in: tuple{true, true}, wantEq: true},
  59. {in: tuple{false, false}, wantEq: true},
  60. {
  61. in: tuple{
  62. scalars{-8, -16, -32, -64, -1234, 8, 16, 32, 64, 1234, 5678, 32.32, 64.64, 32 + 32i, 64 + 64i},
  63. scalars{-8, -16, -32, -64, -1234, 8, 16, 32, 64, 1234, 5678, 32.32, 64.64, 32 + 32i, 64 + 64i},
  64. },
  65. wantEq: true,
  66. },
  67. {in: tuple{scalars{I8: math.MinInt8}, scalars{I8: math.MinInt8 / 2}}, wantEq: false},
  68. {in: tuple{scalars{I16: math.MinInt16}, scalars{I16: math.MinInt16 / 2}}, wantEq: false},
  69. {in: tuple{scalars{I32: math.MinInt32}, scalars{I32: math.MinInt32 / 2}}, wantEq: false},
  70. {in: tuple{scalars{I64: math.MinInt64}, scalars{I64: math.MinInt64 / 2}}, wantEq: false},
  71. {in: tuple{scalars{I: -1234}, scalars{I: -1234 / 2}}, wantEq: false},
  72. {in: tuple{scalars{U8: math.MaxUint8}, scalars{U8: math.MaxUint8 / 2}}, wantEq: false},
  73. {in: tuple{scalars{U16: math.MaxUint16}, scalars{U16: math.MaxUint16 / 2}}, wantEq: false},
  74. {in: tuple{scalars{U32: math.MaxUint32}, scalars{U32: math.MaxUint32 / 2}}, wantEq: false},
  75. {in: tuple{scalars{U64: math.MaxUint64}, scalars{U64: math.MaxUint64 / 2}}, wantEq: false},
  76. {in: tuple{scalars{U: 1234}, scalars{U: 1234 / 2}}, wantEq: false},
  77. {in: tuple{scalars{UP: 5678}, scalars{UP: 5678 / 2}}, wantEq: false},
  78. {in: tuple{scalars{F32: 32.32}, scalars{F32: math.Nextafter32(32.32, 0)}}, wantEq: false},
  79. {in: tuple{scalars{F64: 64.64}, scalars{F64: math.Nextafter(64.64, 0)}}, wantEq: false},
  80. {in: tuple{scalars{F32: float32(math.NaN())}, scalars{F32: float32(math.NaN())}}, wantEq: true},
  81. {in: tuple{scalars{F64: float64(math.NaN())}, scalars{F64: float64(math.NaN())}}, wantEq: true},
  82. {in: tuple{scalars{C64: 32 + 32i}, scalars{C64: complex(math.Nextafter32(32, 0), 32)}}, wantEq: false},
  83. {in: tuple{scalars{C128: 64 + 64i}, scalars{C128: complex(math.Nextafter(64, 0), 64)}}, wantEq: false},
  84. {in: tuple{[]appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}, []appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}}, wantEq: true},
  85. {in: tuple{[]appendBytes{{}, {0, 0, 0, 0, 0, 0, 0, 1}}, []appendBytes{{0, 0, 0, 0, 0, 0, 0, 1}, {}}}, wantEq: false},
  86. {in: tuple{iface{MyBool(true)}, iface{MyBool(true)}}, wantEq: true},
  87. {in: tuple{iface{true}, iface{MyBool(true)}}, wantEq: false},
  88. {in: tuple{iface{MyHeader{}}, iface{MyHeader{}}}, wantEq: true},
  89. {in: tuple{iface{MyHeader{}}, iface{tar.Header{}}}, wantEq: false},
  90. {in: tuple{iface{&MyHeader{}}, iface{&MyHeader{}}}, wantEq: true},
  91. {in: tuple{iface{&MyHeader{}}, iface{&tar.Header{}}}, wantEq: false},
  92. {in: tuple{iface{[]map[string]MyBool{}}, iface{[]map[string]MyBool{}}}, wantEq: true},
  93. {in: tuple{iface{[]map[string]bool{}}, iface{[]map[string]MyBool{}}}, wantEq: false},
  94. {
  95. in: func() tuple {
  96. i1 := 1
  97. i2 := 2
  98. v1 := [3]*int{&i1, &i2, &i1}
  99. v2 := [3]*int{&i1, &i2, &i2}
  100. return tuple{v1, v2}
  101. }(),
  102. wantEq: false,
  103. },
  104. }
  105. for _, tt := range tests {
  106. gotEq := Hash(tt.in[0]) == Hash(tt.in[1])
  107. if gotEq != tt.wantEq {
  108. t.Errorf("(Hash(%v) == Hash(%v)) = %v, want %v", tt.in[0], tt.in[1], gotEq, tt.wantEq)
  109. }
  110. }
  111. }
  112. func TestDeepHash(t *testing.T) {
  113. // v contains the types of values we care about for our current callers.
  114. // Mostly we're just testing that we don't panic on handled types.
  115. v := getVal()
  116. hash1 := Hash(v)
  117. t.Logf("hash: %v", hash1)
  118. for i := 0; i < 20; i++ {
  119. hash2 := Hash(getVal())
  120. if hash1 != hash2 {
  121. t.Error("second hash didn't match")
  122. }
  123. }
  124. }
  125. func getVal() []interface{} {
  126. return []interface{}{
  127. &wgcfg.Config{
  128. Name: "foo",
  129. Addresses: []netaddr.IPPrefix{netaddr.IPPrefixFrom(netaddr.IPFrom16([16]byte{3: 3}), 5)},
  130. Peers: []wgcfg.Peer{
  131. {
  132. PublicKey: key.NodePublic{},
  133. },
  134. },
  135. },
  136. &router.Config{
  137. Routes: []netaddr.IPPrefix{
  138. netaddr.MustParseIPPrefix("1.2.3.0/24"),
  139. netaddr.MustParseIPPrefix("1234::/64"),
  140. },
  141. },
  142. map[dnsname.FQDN][]netaddr.IP{
  143. dnsname.FQDN("a."): {netaddr.MustParseIP("1.2.3.4"), netaddr.MustParseIP("4.3.2.1")},
  144. dnsname.FQDN("b."): {netaddr.MustParseIP("8.8.8.8"), netaddr.MustParseIP("9.9.9.9")},
  145. dnsname.FQDN("c."): {netaddr.MustParseIP("6.6.6.6"), netaddr.MustParseIP("7.7.7.7")},
  146. dnsname.FQDN("d."): {netaddr.MustParseIP("6.7.6.6"), netaddr.MustParseIP("7.7.7.8")},
  147. dnsname.FQDN("e."): {netaddr.MustParseIP("6.8.6.6"), netaddr.MustParseIP("7.7.7.9")},
  148. dnsname.FQDN("f."): {netaddr.MustParseIP("6.9.6.6"), netaddr.MustParseIP("7.7.7.0")},
  149. },
  150. map[dnsname.FQDN][]netaddr.IPPort{
  151. dnsname.FQDN("a."): {netaddr.MustParseIPPort("1.2.3.4:11"), netaddr.MustParseIPPort("4.3.2.1:22")},
  152. dnsname.FQDN("b."): {netaddr.MustParseIPPort("8.8.8.8:11"), netaddr.MustParseIPPort("9.9.9.9:22")},
  153. dnsname.FQDN("c."): {netaddr.MustParseIPPort("8.8.8.8:12"), netaddr.MustParseIPPort("9.9.9.9:23")},
  154. dnsname.FQDN("d."): {netaddr.MustParseIPPort("8.8.8.8:13"), netaddr.MustParseIPPort("9.9.9.9:24")},
  155. dnsname.FQDN("e."): {netaddr.MustParseIPPort("8.8.8.8:14"), netaddr.MustParseIPPort("9.9.9.9:25")},
  156. },
  157. map[key.DiscoPublic]bool{
  158. key.DiscoPublicFromRaw32(mem.B([]byte{1: 1, 31: 0})): true,
  159. key.DiscoPublicFromRaw32(mem.B([]byte{1: 2, 31: 0})): false,
  160. key.DiscoPublicFromRaw32(mem.B([]byte{1: 3, 31: 0})): true,
  161. key.DiscoPublicFromRaw32(mem.B([]byte{1: 4, 31: 0})): false,
  162. },
  163. &tailcfg.MapResponse{
  164. DERPMap: &tailcfg.DERPMap{
  165. Regions: map[int]*tailcfg.DERPRegion{
  166. 1: &tailcfg.DERPRegion{
  167. RegionID: 1,
  168. RegionCode: "foo",
  169. Nodes: []*tailcfg.DERPNode{
  170. {
  171. Name: "n1",
  172. RegionID: 1,
  173. HostName: "foo.com",
  174. },
  175. {
  176. Name: "n2",
  177. RegionID: 1,
  178. HostName: "bar.com",
  179. },
  180. },
  181. },
  182. },
  183. },
  184. DNSConfig: &tailcfg.DNSConfig{
  185. Resolvers: []dnstype.Resolver{
  186. {Addr: "10.0.0.1"},
  187. },
  188. },
  189. PacketFilter: []tailcfg.FilterRule{
  190. {
  191. SrcIPs: []string{"1.2.3.4"},
  192. DstPorts: []tailcfg.NetPortRange{
  193. {
  194. IP: "1.2.3.4/32",
  195. Ports: tailcfg.PortRange{First: 1, Last: 2},
  196. },
  197. },
  198. },
  199. },
  200. Peers: []*tailcfg.Node{
  201. {
  202. ID: 1,
  203. },
  204. {
  205. ID: 2,
  206. },
  207. },
  208. UserProfiles: []tailcfg.UserProfile{
  209. {ID: 1, LoginName: "[email protected]"},
  210. {ID: 2, LoginName: "[email protected]"},
  211. },
  212. },
  213. filter.Match{
  214. IPProto: []ipproto.Proto{1, 2, 3},
  215. },
  216. }
  217. }
  218. var sink = Hash("foo")
  219. func BenchmarkHash(b *testing.B) {
  220. b.ReportAllocs()
  221. v := getVal()
  222. for i := 0; i < b.N; i++ {
  223. sink = Hash(v)
  224. }
  225. }
  226. func TestHashMapAcyclic(t *testing.T) {
  227. m := map[int]string{}
  228. for i := 0; i < 100; i++ {
  229. m[i] = fmt.Sprint(i)
  230. }
  231. got := map[string]bool{}
  232. var buf bytes.Buffer
  233. bw := bufio.NewWriter(&buf)
  234. for i := 0; i < 20; i++ {
  235. v := reflect.ValueOf(m)
  236. buf.Reset()
  237. bw.Reset(&buf)
  238. h := &hasher{bw: bw}
  239. h.hashMap(v)
  240. if got[string(buf.Bytes())] {
  241. continue
  242. }
  243. got[string(buf.Bytes())] = true
  244. }
  245. if len(got) != 1 {
  246. t.Errorf("got %d results; want 1", len(got))
  247. }
  248. }
  249. func TestPrintArray(t *testing.T) {
  250. type T struct {
  251. X [32]byte
  252. }
  253. x := T{X: [32]byte{1: 1, 31: 31}}
  254. var got bytes.Buffer
  255. bw := bufio.NewWriter(&got)
  256. h := &hasher{bw: bw}
  257. h.hashValue(reflect.ValueOf(x))
  258. bw.Flush()
  259. 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"
  260. if got := got.Bytes(); string(got) != want {
  261. t.Errorf("wrong:\n got: %q\nwant: %q\n", got, want)
  262. }
  263. }
  264. func BenchmarkHashMapAcyclic(b *testing.B) {
  265. b.ReportAllocs()
  266. m := map[int]string{}
  267. for i := 0; i < 100; i++ {
  268. m[i] = fmt.Sprint(i)
  269. }
  270. var buf bytes.Buffer
  271. bw := bufio.NewWriter(&buf)
  272. v := reflect.ValueOf(m)
  273. h := &hasher{bw: bw}
  274. for i := 0; i < b.N; i++ {
  275. buf.Reset()
  276. bw.Reset(&buf)
  277. h.hashMap(v)
  278. }
  279. }
  280. func BenchmarkTailcfgNode(b *testing.B) {
  281. b.ReportAllocs()
  282. node := new(tailcfg.Node)
  283. for i := 0; i < b.N; i++ {
  284. sink = Hash(node)
  285. }
  286. }
  287. func TestExhaustive(t *testing.T) {
  288. seen := make(map[Sum]bool)
  289. for i := 0; i < 100000; i++ {
  290. s := Hash(i)
  291. if seen[s] {
  292. t.Fatalf("hash collision %v", i)
  293. }
  294. seen[s] = true
  295. }
  296. }
  297. // verify this doesn't loop forever, as it used to (Issue 2340)
  298. func TestMapCyclicFallback(t *testing.T) {
  299. type T struct {
  300. M map[string]interface{}
  301. }
  302. v := &T{
  303. M: map[string]interface{}{},
  304. }
  305. v.M["m"] = v.M
  306. Hash(v)
  307. }
  308. func TestArrayAllocs(t *testing.T) {
  309. if version.IsRace() {
  310. t.Skip("skipping test under race detector")
  311. }
  312. // In theory, there should be no allocations. However, escape analysis on
  313. // certain architectures fails to detect that certain cases do not escape.
  314. // This discrepency currently affects sha256.digest.Sum.
  315. // Measure the number of allocations in sha256 to ensure that Hash does
  316. // not allocate on top of its usage of sha256.
  317. // See https://golang.org/issue/48055.
  318. var b []byte
  319. h := sha256.New()
  320. want := int(testing.AllocsPerRun(1000, func() {
  321. b = h.Sum(b[:0])
  322. }))
  323. switch runtime.GOARCH {
  324. case "amd64", "arm64":
  325. want = 0 // ensure no allocations on popular architectures
  326. }
  327. type T struct {
  328. X [32]byte
  329. }
  330. x := &T{X: [32]byte{1: 1, 2: 2, 3: 3, 4: 4}}
  331. got := int(testing.AllocsPerRun(1000, func() {
  332. sink = Hash(x)
  333. }))
  334. if got > want {
  335. t.Errorf("allocs = %v; want %v", got, want)
  336. }
  337. }
  338. func BenchmarkHashArray(b *testing.B) {
  339. b.ReportAllocs()
  340. type T struct {
  341. X [32]byte
  342. }
  343. x := &T{X: [32]byte{1: 1, 2: 2, 3: 3, 4: 4}}
  344. for i := 0; i < b.N; i++ {
  345. sink = Hash(x)
  346. }
  347. }