mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 09:25:11 +00:00 
			
		
		
		
	util/deephash: prevent infinite loop on map cycle
Fixes #2340 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
		
				
					committed by
					
						
						Brad Fitzpatrick
					
				
			
			
				
	
			
			
			
						parent
						
							aceaa70b16
						
					
				
				
					commit
					3962744450
				
			@@ -169,6 +169,28 @@ func (h *hasher) print(v reflect.Value) (acyclic bool) {
 | 
				
			|||||||
	case reflect.Interface:
 | 
						case reflect.Interface:
 | 
				
			||||||
		return h.print(v.Elem())
 | 
							return h.print(v.Elem())
 | 
				
			||||||
	case reflect.Map:
 | 
						case reflect.Map:
 | 
				
			||||||
 | 
							// TODO(bradfitz): ideally we'd avoid these map
 | 
				
			||||||
 | 
							// operations to detect cycles if we knew from the map
 | 
				
			||||||
 | 
							// element type that there no way to form a cycle,
 | 
				
			||||||
 | 
							// which is the common case. Notably, we don't care
 | 
				
			||||||
 | 
							// about hashing the same map+contents twice in
 | 
				
			||||||
 | 
							// different parts of the tree. In fact, we should
 | 
				
			||||||
 | 
							// ideally. (And this prevents it) We should only stop
 | 
				
			||||||
 | 
							// hashing when there's a cycle.  What we should
 | 
				
			||||||
 | 
							// probably do is make sure we enumerate the data
 | 
				
			||||||
 | 
							// structure tree is a fixed order and then give each
 | 
				
			||||||
 | 
							// pointer an increasing number, and when we hit a
 | 
				
			||||||
 | 
							// dup, rather than emitting nothing, we should emit a
 | 
				
			||||||
 | 
							// "value #12" reference. Which implies that all things
 | 
				
			||||||
 | 
							// emit to the bufio.Writer should be type-tagged so
 | 
				
			||||||
 | 
							// we can distinguish loop references without risk of
 | 
				
			||||||
 | 
							// collisions.
 | 
				
			||||||
 | 
							ptr := v.Pointer()
 | 
				
			||||||
 | 
							if visited[ptr] {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							visited[ptr] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if h.hashMapAcyclic(v) {
 | 
							if h.hashMapAcyclic(v) {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -226,3 +226,15 @@ func TestSHA256EqualHex(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// verify this doesn't loop forever, as it used to (Issue 2340)
 | 
				
			||||||
 | 
					func TestMapCyclicFallback(t *testing.T) {
 | 
				
			||||||
 | 
						type T struct {
 | 
				
			||||||
 | 
							M map[string]interface{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						v := &T{
 | 
				
			||||||
 | 
							M: map[string]interface{}{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						v.M["m"] = v.M
 | 
				
			||||||
 | 
						Hash(v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user