Correctly lock when removing neighbor entries

Fix a panic when two entries in Failed state are removed at the same time.

PiperOrigin-RevId: 344143777
This commit is contained in:
Sam Balana
2020-11-24 15:37:47 -08:00
committed by gVisor bot
parent 4da63dc82e
commit 99f2d0ea2f
3 changed files with 26 additions and 2 deletions
+2
View File
@@ -233,6 +233,8 @@ func (n *neighborCache) addStaticEntry(addr tcpip.Address, linkAddr tcpip.LinkAd
}
// removeEntryLocked removes the specified entry from the neighbor cache.
//
// Prerequisite: n.mu and entry.mu MUST be locked.
func (n *neighborCache) removeEntryLocked(entry *neighborEntry) {
if entry.neigh.State != Static {
n.dynamic.lru.Remove(entry)
+3 -1
View File
@@ -80,7 +80,7 @@ func entryDiffOptsWithSort() []cmp.Option {
func newTestNeighborCache(nudDisp NUDDispatcher, config NUDConfigurations, clock tcpip.Clock) *neighborCache {
config.resetInvalidFields()
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
return &neighborCache{
neigh := &neighborCache{
nic: &NIC{
stack: &Stack{
clock: clock,
@@ -92,6 +92,8 @@ func newTestNeighborCache(nudDisp NUDDispatcher, config NUDConfigurations, clock
state: NewNUDState(config, rng),
cache: make(map[tcpip.Address]*neighborEntry, neighborCacheSize),
}
neigh.nic.neigh = neigh
return neigh
}
// testEntryStore contains a set of IP to NeighborEntry mappings.
+21 -1
View File
@@ -258,7 +258,7 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
case Failed:
e.notifyWakersLocked()
e.job = e.nic.stack.newJob(&e.mu, func() {
e.job = e.nic.stack.newJob(&doubleLock{first: &e.nic.neigh.mu, second: &e.mu}, func() {
e.nic.neigh.removeEntryLocked(e)
})
e.job.Schedule(config.UnreachableTime)
@@ -512,3 +512,23 @@ func (e *neighborEntry) handleUpperLevelConfirmationLocked() {
panic(fmt.Sprintf("Invalid cache entry state: %s", e.neigh.State))
}
}
// doubleLock combines two locks into one while maintaining lock ordering.
//
// TODO(gvisor.dev/issue/4796): Remove this once subsequent traffic to a Failed
// neighbor is allowed.
type doubleLock struct {
first, second sync.Locker
}
// Lock locks both locks in order: first then second.
func (l *doubleLock) Lock() {
l.first.Lock()
l.second.Lock()
}
// Unlock unlocks both locks in reverse order: second then first.
func (l *doubleLock) Unlock() {
l.second.Unlock()
l.first.Unlock()
}