Skip to content

Commit

Permalink
ghost: Fix Session race conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
lukechampine committed Oct 15, 2020
1 parent 8feee69 commit 2356267
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 28 deletions.
2 changes: 2 additions & 0 deletions internal/ghost/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/ed25519"
"log"
"net"
"sync"

"gitlab.com/NebulousLabs/Sia/crypto"
"gitlab.com/NebulousLabs/Sia/modules"
Expand All @@ -23,6 +24,7 @@ type hostContract struct {
renterKey types.SiaPublicKey
sectorRoots []crypto.Hash
sectorData map[crypto.Hash][renterhost.SectorSize]byte
mu sync.Mutex
}

type Host struct {
Expand Down
45 changes: 17 additions & 28 deletions internal/ghost/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func (h *Host) handleConn(conn net.Conn) error {
sess: hs,
conn: conn,
}
defer func() {
if s.contract != nil {
s.contract.mu.Unlock()
s.contract = nil
}
}()

rpcs := map[renterhost.Specifier]func(*session) error{
renterhost.RPCFormContractID: h.rpcFormContract,
Expand Down Expand Up @@ -278,6 +284,7 @@ func (h *Host) rpcLock(s *session) error {
s.sess.WriteResponse(nil, err)
return err
}
contract.mu.Lock()
s.contract = contract

var newChallenge [16]byte
Expand All @@ -293,6 +300,9 @@ func (h *Host) rpcLock(s *session) error {
}

func (h *Host) rpcUnlock(s *session) error {
if s.contract != nil {
s.contract.mu.Unlock()
}
s.contract = nil
return nil
}
Expand Down Expand Up @@ -565,31 +575,20 @@ func (h *Host) rpcRead(s *session) error {
return err
}

// As soon as we finish reading the request, we must begin listening for
// RPCLoopReadStop, which may arrive at any time, and must arrive before the
// RPC is considered complete.
stopSignal := make(chan error, 1)
go func() {
var id renterhost.Specifier
err := s.sess.ReadResponse(&id, 4096)
if err != nil {
stopSignal <- err
} else if id != renterhost.RPCReadStop {
stopSignal <- errors.New("expected 'stop' from renter, got " + id.String())
} else {
stopSignal <- nil
}
}()
// Make sure we read RPCLoopReadStop before returning.
//
// NOTE: technically, we should be listening for RPCLoopReadStop
// asynchronously, but currently this is not possible because
// renterhost.Session is not thread-safe.
defer s.sess.ReadResponse(new(renterhost.Specifier), 4096)

if s.contract == nil {
err := errors.New("no contract locked")
s.sess.WriteResponse(nil, err)
<-stopSignal
return err
} else if s.contract.rev.NewRevisionNumber == math.MaxUint64 {
err := errors.New("contract cannot be revised")
s.sess.WriteResponse(nil, err)
<-stopSignal
return err
}

Expand Down Expand Up @@ -682,22 +681,12 @@ func (h *Host) rpcRead(s *session) error {
Data: data,
MerkleProof: proof,
}
select {
case err := <-stopSignal:
if err != nil {
return err
}
resp.Signature = hostSig
return s.sess.WriteResponse(resp, nil)
default:
}
if i == len(req.Sections)-1 {
resp.Signature = hostSig
}
if err := s.sess.WriteResponse(resp, nil); err != nil {
return err
}
}
// The stop signal must arrive before RPC is complete.
return <-stopSignal
return nil
}

0 comments on commit 2356267

Please sign in to comment.