diff options
author | Kevin Krakauer <krakauer@google.com> | 2021-02-25 13:34:01 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-02-25 13:35:44 -0800 |
commit | 38c42bbf4ad2200f0ec72e51d1cfbbd83d782a63 (patch) | |
tree | 10db75d71fa93f329cca518d6a14a0a54a17037b /pkg/tcpip/stack | |
parent | e50ee26207a99930be966bd48e04f5bccd85cc05 (diff) |
Remove deadlock in raw.endpoint caused by recursive read locking
Prevents the following deadlock:
- Raw packet is sent via e.Write(), which read locks e.mu
- Connect() is called, blocking on write locking e.mu
- The packet is routed to loopback and back to e.HandlePacket(), which read
locks e.mu
Per the atomic.RWMutex documentation, this deadlocks:
"If a goroutine holds a RWMutex for reading and another goroutine might call
Lock, no goroutine should expect to be able to acquire a read lock until the
initial read lock is released. In particular, this prohibits recursive read
locking. This is to ensure that the lock eventually becomes available; a blocked
Lock call excludes new readers from acquiring the lock."
Also, release eps.mu earlier in deliverRawPacket.
PiperOrigin-RevId: 359600926
Diffstat (limited to 'pkg/tcpip/stack')
-rw-r--r-- | pkg/tcpip/stack/transport_demuxer.go | 11 |
1 files changed, 6 insertions, 5 deletions
diff --git a/pkg/tcpip/stack/transport_demuxer.go b/pkg/tcpip/stack/transport_demuxer.go index 292e51d20..61cbaf688 100644 --- a/pkg/tcpip/stack/transport_demuxer.go +++ b/pkg/tcpip/stack/transport_demuxer.go @@ -582,17 +582,18 @@ func (d *transportDemuxer) deliverRawPacket(protocol tcpip.TransportProtocolNumb // As in net/ipv4/ip_input.c:ip_local_deliver, attempt to deliver via // raw endpoint first. If there are multiple raw endpoints, they all // receive the packet. - foundRaw := false eps.mu.RLock() - for _, rawEP := range eps.rawEndpoints { + // Copy the list of raw endpoints so we can release eps.mu earlier. + rawEPs := make([]RawTransportEndpoint, len(eps.rawEndpoints)) + copy(rawEPs, eps.rawEndpoints) + eps.mu.RUnlock() + for _, rawEP := range rawEPs { // Each endpoint gets its own copy of the packet for the sake // of save/restore. rawEP.HandlePacket(pkt.Clone()) - foundRaw = true } - eps.mu.RUnlock() - return foundRaw + return len(rawEPs) > 0 } // deliverError attempts to deliver the given error to the appropriate transport |