1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/libnetwork/sandboxdata.go
Jana Radhakrishnan 092437ad0e Fix data race in controller sandboxes
The controller sandboxes hashmap is not being protected by a lock
while deleting it in `LeaveAll` call. This may result in a race
whereby any other read access that happens with the lock held is
also vulnerable to return random sandbox data which could result
in totally unpredictable behavior.

Also as part of the fix check if `s.endpoints` is empty and log an
error in `rmEndpoint` so that we don't bring down the process
for this unexpected error.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-07-29 23:57:50 -07:00

259 lines
5 KiB
Go

package libnetwork
import (
"container/heap"
"fmt"
"sync"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/sandbox"
)
type epHeap []*endpoint
type sandboxData struct {
sbox sandbox.Sandbox
refCnt int
endpoints epHeap
sync.Mutex
}
func (eh epHeap) Len() int { return len(eh) }
func (eh epHeap) Less(i, j int) bool {
eh[i].Lock()
eh[j].Lock()
defer eh[j].Unlock()
defer eh[i].Unlock()
if eh[i].container.config.prio == eh[j].container.config.prio {
return eh[i].network.Name() < eh[j].network.Name()
}
return eh[i].container.config.prio > eh[j].container.config.prio
}
func (eh epHeap) Swap(i, j int) { eh[i], eh[j] = eh[j], eh[i] }
func (eh *epHeap) Push(x interface{}) {
*eh = append(*eh, x.(*endpoint))
}
func (eh *epHeap) Pop() interface{} {
old := *eh
n := len(old)
x := old[n-1]
*eh = old[0 : n-1]
return x
}
func (s *sandboxData) updateGateway(ep *endpoint) error {
sb := s.sandbox()
sb.UnsetGateway()
sb.UnsetGatewayIPv6()
if ep == nil {
return nil
}
ep.Lock()
joinInfo := ep.joinInfo
ep.Unlock()
if err := sb.SetGateway(joinInfo.gw); err != nil {
return fmt.Errorf("failed to set gateway while updating gateway: %v", err)
}
if err := sb.SetGatewayIPv6(joinInfo.gw6); err != nil {
return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err)
}
return nil
}
func (s *sandboxData) addEndpoint(ep *endpoint) error {
ep.Lock()
joinInfo := ep.joinInfo
ifaces := ep.iFaces
ep.Unlock()
sb := s.sandbox()
for _, i := range ifaces {
var ifaceOptions []sandbox.IfaceOption
ifaceOptions = append(ifaceOptions, sb.InterfaceOptions().Address(&i.addr),
sb.InterfaceOptions().Routes(i.routes))
if i.addrv6.IP.To16() != nil {
ifaceOptions = append(ifaceOptions,
sb.InterfaceOptions().AddressIPv6(&i.addrv6))
}
if err := sb.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil {
return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err)
}
}
if joinInfo != nil {
// Set up non-interface routes.
for _, r := range ep.joinInfo.StaticRoutes {
if err := sb.AddStaticRoute(r); err != nil {
return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err)
}
}
}
s.Lock()
heap.Push(&s.endpoints, ep)
highEp := s.endpoints[0]
s.Unlock()
if ep == highEp {
if err := s.updateGateway(ep); err != nil {
return err
}
}
return nil
}
func (s *sandboxData) rmEndpoint(ep *endpoint) {
ep.Lock()
joinInfo := ep.joinInfo
ep.Unlock()
sb := s.sandbox()
for _, i := range sb.Info().Interfaces() {
// Only remove the interfaces owned by this endpoint from the sandbox.
if ep.hasInterface(i.SrcName()) {
if err := i.Remove(); err != nil {
logrus.Debugf("Remove interface failed: %v", err)
}
}
}
// Remove non-interface routes.
for _, r := range joinInfo.StaticRoutes {
if err := sb.RemoveStaticRoute(r); err != nil {
logrus.Debugf("Remove route failed: %v", err)
}
}
s.Lock()
if len(s.endpoints) == 0 {
// s.endpoints should never be empty and this is unexpected error condition
// We log an error message to note this down for debugging purposes.
logrus.Errorf("No endpoints in sandbox while trying to remove endpoint %s", ep.Name())
s.Unlock()
return
}
highEpBefore := s.endpoints[0]
var (
i int
e *endpoint
)
for i, e = range s.endpoints {
if e == ep {
break
}
}
heap.Remove(&s.endpoints, i)
var highEpAfter *endpoint
if len(s.endpoints) > 0 {
highEpAfter = s.endpoints[0]
}
s.Unlock()
if highEpBefore != highEpAfter {
s.updateGateway(highEpAfter)
}
}
func (s *sandboxData) sandbox() sandbox.Sandbox {
s.Lock()
defer s.Unlock()
return s.sbox
}
func (c *controller) sandboxAdd(key string, create bool, ep *endpoint) (sandbox.Sandbox, error) {
c.Lock()
sData, ok := c.sandboxes[key]
c.Unlock()
if !ok {
sb, err := sandbox.NewSandbox(key, create)
if err != nil {
return nil, fmt.Errorf("failed to create new sandbox: %v", err)
}
sData = &sandboxData{
sbox: sb,
endpoints: epHeap{},
}
heap.Init(&sData.endpoints)
c.Lock()
c.sandboxes[key] = sData
c.Unlock()
}
if err := sData.addEndpoint(ep); err != nil {
return nil, err
}
return sData.sandbox(), nil
}
func (c *controller) sandboxRm(key string, ep *endpoint) {
c.Lock()
sData := c.sandboxes[key]
c.Unlock()
sData.rmEndpoint(ep)
}
func (c *controller) sandboxGet(key string) sandbox.Sandbox {
c.Lock()
sData, ok := c.sandboxes[key]
c.Unlock()
if !ok {
return nil
}
return sData.sandbox()
}
func (c *controller) LeaveAll(id string) error {
c.Lock()
sData, ok := c.sandboxes[sandbox.GenerateKey(id)]
c.Unlock()
if !ok {
return fmt.Errorf("could not find sandbox for container id %s", id)
}
sData.Lock()
eps := make([]*endpoint, len(sData.endpoints))
for i, ep := range sData.endpoints {
eps[i] = ep
}
sData.Unlock()
for _, ep := range eps {
if err := ep.Leave(id); err != nil {
logrus.Warnf("Failed leaving endpoint id %s: %v\n", ep.ID(), err)
}
}
sData.sandbox().Destroy()
c.Lock()
delete(c.sandboxes, sandbox.GenerateKey(id))
c.Unlock()
return nil
}