From 60dd97c7fc5f8f114ebebf9fe78cbb39b6672e76 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 8 Jul 2021 21:18:59 +0300 Subject: [PATCH] fix potential deadlock in View As pointed out by magik, it is possible to deadlock if the view callback performs a blockstore operation while a Lock is pending. This fixes the issue by optimistically tracking the reference before actually calling the underlying View and limiting the scope of the lock. --- blockstore/splitstore/splitstore.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/blockstore/splitstore/splitstore.go b/blockstore/splitstore/splitstore.go index c02927d36..30d108656 100644 --- a/blockstore/splitstore/splitstore.go +++ b/blockstore/splitstore/splitstore.go @@ -376,19 +376,19 @@ func (s *SplitStore) HashOnRead(enabled bool) { } func (s *SplitStore) View(cid cid.Cid, cb func([]byte) error) error { + // optimistically protect the reference so that we can call the underlying View + // without holding hte lock. + // This allows the user callback to call into the blockstore without deadlocking. s.txnLk.RLock() - defer s.txnLk.RUnlock() + err := s.trackTxnRef(cid) + s.txnLk.RUnlock() - err := s.hot.View(cid, cb) + if err != nil { + log.Warnf("error tracking reference to %s: %s", cid, err) + } + + err = s.hot.View(cid, cb) switch err { - case nil: - err = s.trackTxnRef(cid) - if err != nil { - log.Warnf("error tracking reference to %s: %s", cid, err) - } - - return nil - case bstore.ErrNotFound: if s.debug != nil { s.mx.Lock()