From af4080b4b7fe653568714b0012fc702ecec917f4 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 15 Apr 2020 11:07:29 +0200 Subject: [PATCH] trie: fix concurrent usage of secKeyBuf, ref #20920 --- trie/database.go | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/trie/database.go b/trie/database.go index 5b673938f..5d7a4fd91 100644 --- a/trie/database.go +++ b/trie/database.go @@ -59,8 +59,11 @@ var ( // secureKeyPrefix is the database key prefix used to store trie node preimages. var secureKeyPrefix = []byte("secure-key-") +// secureKeyPrefixLength is the length of the above prefix +const secureKeyPrefixLength = 11 + // secureKeyLength is the length of the above prefix + 32byte hash. -const secureKeyLength = 11 + 32 +const secureKeyLength = secureKeyPrefixLength + 32 // Database is an intermediate write layer between the trie data structures and // the disk database. The aim is to accumulate trie writes in-memory and only @@ -79,7 +82,6 @@ type Database struct { newest common.Hash // Newest tracked node, flush-list tail preimages map[common.Hash][]byte // Preimages of nodes from the secure trie - seckeybuf [secureKeyLength]byte // Ephemeral buffer for calculating preimage keys gctime time.Duration // Time spent on garbage collection since last commit gcnodes uint64 // Nodes garbage collected since last commit @@ -445,15 +447,15 @@ func (db *Database) preimage(hash common.Hash) ([]byte, error) { return preimage, nil } // Content unavailable in memory, attempt to retrieve from disk - return db.diskdb.Get(db.secureKey(hash[:])) + return db.diskdb.Get(secureKey(hash)) } -// secureKey returns the database key for the preimage of key, as an ephemeral -// buffer. The caller must not hold onto the return value because it will become -// invalid on the next call. -func (db *Database) secureKey(key []byte) []byte { - buf := append(db.seckeybuf[:0], secureKeyPrefix...) - buf = append(buf, key...) +// secureKey returns the database key for the preimage of key (as a newly +// allocated byte-slice) +func secureKey(hash common.Hash) []byte { + buf := make([]byte, secureKeyLength) + copy(buf, secureKeyPrefix) + copy(buf[secureKeyPrefixLength:], hash[:]) return buf } @@ -596,12 +598,18 @@ func (db *Database) Cap(limit common.StorageSize) error { size := db.dirtiesSize + common.StorageSize((len(db.dirties)-1)*cachedNodeSize) size += db.childrenSize - common.StorageSize(len(db.dirties[common.Hash{}].children)*(common.HashLength+2)) + // We reuse an ephemeral buffer for the keys. The batch Put operation + // copies it internally, so we can reuse it. + var keyBuf [secureKeyLength]byte + copy(keyBuf[:], secureKeyPrefix) + // If the preimage cache got large enough, push to disk. If it's still small // leave for later to deduplicate writes. flushPreimages := db.preimagesSize > 4*1024*1024 if flushPreimages { for hash, preimage := range db.preimages { - if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil { + copy(keyBuf[secureKeyPrefixLength:], hash[:]) + if err := batch.Put(keyBuf[:], preimage); err != nil { log.Error("Failed to commit preimage from trie database", "err", err) return err } @@ -692,9 +700,15 @@ func (db *Database) Commit(node common.Hash, report bool) error { start := time.Now() batch := db.diskdb.NewBatch() + // We reuse an ephemeral buffer for the keys. The batch Put operation + // copies it internally, so we can reuse it. + var keyBuf [secureKeyLength]byte + copy(keyBuf[:], secureKeyPrefix) + // Move all of the accumulated preimages into a write batch for hash, preimage := range db.preimages { - if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil { + copy(keyBuf[secureKeyPrefixLength:], hash[:]) + if err := batch.Put(keyBuf[:], preimage); err != nil { log.Error("Failed to commit preimage from trie database", "err", err) return err }