mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Fix datarace on TransportEndpointInfo.ID and clean up semantics.
Ensures that all access to TransportEndpointInfo.ID is either: * In a function ending in a Locked suffix. * While holding the appropriate mutex. This primary affects the checkV4Mapped method on affected endpoints, which has been renamed to checkV4MappedLocked. Also document the method and change its argument to be a value instead of a pointer which had caused some awkwardness. This race was possible in the udp and icmp endpoints between Connect and uses of TransportEndpointInfo.ID including in both itself and Bind. The tcp endpoint did not suffer from this bug, but benefited from better documentation. Updates #357 PiperOrigin-RevId: 298682913
This commit is contained in:
@@ -551,11 +551,13 @@ type TransportEndpointInfo struct {
|
||||
RegisterNICID tcpip.NICID
|
||||
}
|
||||
|
||||
// AddrNetProto unwraps the specified address if it is a V4-mapped V6 address
|
||||
// and returns the network protocol number to be used to communicate with the
|
||||
// specified address. It returns an error if the passed address is incompatible
|
||||
// with the receiver.
|
||||
func (e *TransportEndpointInfo) AddrNetProto(addr tcpip.FullAddress, v6only bool) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
// AddrNetProtoLocked unwraps the specified address if it is a V4-mapped V6
|
||||
// address and returns the network protocol number to be used to communicate
|
||||
// with the specified address. It returns an error if the passed address is
|
||||
// incompatible with the receiver.
|
||||
//
|
||||
// Preconditon: the parent endpoint mu must be held while calling this method.
|
||||
func (e *TransportEndpointInfo) AddrNetProtoLocked(addr tcpip.FullAddress, v6only bool) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
netProto := e.NetProto
|
||||
switch len(addr.Addr) {
|
||||
case header.IPv4AddressSize:
|
||||
|
||||
@@ -291,15 +291,13 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
|
||||
nicID = e.BindNICID
|
||||
}
|
||||
|
||||
toCopy := *to
|
||||
to = &toCopy
|
||||
netProto, err := e.checkV4Mapped(to)
|
||||
dst, netProto, err := e.checkV4MappedLocked(*to)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Find the enpoint.
|
||||
r, err := e.stack.FindRoute(nicID, e.BindAddr, to.Addr, netProto, false /* multicastLoop */)
|
||||
// Find the endpoint.
|
||||
r, err := e.stack.FindRoute(nicID, e.BindAddr, dst.Addr, netProto, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -480,13 +478,14 @@ func send6(r *stack.Route, ident uint16, data buffer.View, ttl uint8) *tcpip.Err
|
||||
})
|
||||
}
|
||||
|
||||
func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress) (tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProto(*addr, false /* v6only */)
|
||||
// checkV4MappedLocked determines the effective network protocol and converts
|
||||
// addr to its canonical form.
|
||||
func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, false /* v6only */)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return tcpip.FullAddress{}, 0, err
|
||||
}
|
||||
*addr = unwrapped
|
||||
return netProto, nil
|
||||
return unwrapped, netProto, nil
|
||||
}
|
||||
|
||||
// Disconnect implements tcpip.Endpoint.Disconnect.
|
||||
@@ -517,7 +516,7 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
netProto, err := e.checkV4Mapped(&addr)
|
||||
addr, netProto, err := e.checkV4MappedLocked(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -630,7 +629,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
netProto, err := e.checkV4Mapped(&addr)
|
||||
addr, netProto, err := e.checkV4MappedLocked(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1874,13 +1874,14 @@ func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress) (tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProto(*addr, e.v6only)
|
||||
// checkV4MappedLocked determines the effective network protocol and converts
|
||||
// addr to its canonical form.
|
||||
func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, e.v6only)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return tcpip.FullAddress{}, 0, err
|
||||
}
|
||||
*addr = unwrapped
|
||||
return netProto, nil
|
||||
return unwrapped, netProto, nil
|
||||
}
|
||||
|
||||
// Disconnect implements tcpip.Endpoint.Disconnect.
|
||||
@@ -1910,7 +1911,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
|
||||
|
||||
connectingAddr := addr.Addr
|
||||
|
||||
netProto, err := e.checkV4Mapped(&addr)
|
||||
addr, netProto, err := e.checkV4MappedLocked(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2276,7 +2277,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err *tcpip.Error) {
|
||||
}
|
||||
|
||||
e.BindAddr = addr.Addr
|
||||
netProto, err := e.checkV4Mapped(&addr)
|
||||
addr, netProto, err := e.checkV4MappedLocked(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -443,19 +443,19 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
|
||||
return 0, nil, tcpip.ErrBroadcastDisabled
|
||||
}
|
||||
|
||||
netProto, err := e.checkV4Mapped(to)
|
||||
dst, netProto, err := e.checkV4MappedLocked(*to)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
r, _, err := e.connectRoute(nicID, *to, netProto)
|
||||
r, _, err := e.connectRoute(nicID, dst, netProto)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
route = &r
|
||||
dstPort = to.Port
|
||||
dstPort = dst.Port
|
||||
}
|
||||
|
||||
if route.IsResolutionRequired() {
|
||||
@@ -566,7 +566,7 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
|
||||
defer e.mu.Unlock()
|
||||
|
||||
fa := tcpip.FullAddress{Addr: v.InterfaceAddr}
|
||||
netProto, err := e.checkV4Mapped(&fa)
|
||||
fa, netProto, err := e.checkV4MappedLocked(fa)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -927,13 +927,14 @@ func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort u
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress) (tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProto(*addr, e.v6only)
|
||||
// checkV4MappedLocked determines the effective network protocol and converts
|
||||
// addr to its canonical form.
|
||||
func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, e.v6only)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return tcpip.FullAddress{}, 0, err
|
||||
}
|
||||
*addr = unwrapped
|
||||
return netProto, nil
|
||||
return unwrapped, netProto, nil
|
||||
}
|
||||
|
||||
// Disconnect implements tcpip.Endpoint.Disconnect.
|
||||
@@ -981,10 +982,6 @@ func (e *endpoint) Disconnect() *tcpip.Error {
|
||||
|
||||
// Connect connects the endpoint to its peer. Specifying a NIC is optional.
|
||||
func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
|
||||
netProto, err := e.checkV4Mapped(&addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addr.Port == 0 {
|
||||
// We don't support connecting to port zero.
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
@@ -1012,6 +1009,11 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
addr, netProto, err := e.checkV4MappedLocked(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, nicID, err := e.connectRoute(nicID, addr, netProto)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1139,7 +1141,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
netProto, err := e.checkV4Mapped(&addr)
|
||||
addr, netProto, err := e.checkV4MappedLocked(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ func (e *endpoint) afterLoad() {
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *endpoint) Resume(s *stack.Stack) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
e.stack = s
|
||||
|
||||
for _, m := range e.multicastMemberships {
|
||||
|
||||
Reference in New Issue
Block a user