mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-14 16:03:45 +00:00
eth: don't read a blob pool that is still initialising (#35509)
`BlobPool.Init` builds the lookup, index and store without holding
`p.lock`, and `eth/backend.go` publishes the pool to `blobpool.NewCache`
before `txpool.New` runs Init. Cache.update spawns a goroutine calling
`getByVhash`, so it can read the pool mid-construction:
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x28]
blobpool.(*BlobPool).getByVhash(...) blobpool.go:1303
blobpool.(*Cache).update.func1() cache.go:429
```
`Init` fills the lookup inside `billy.Open`, whose index callback runs
`parseTransaction` -> `trackTransaction`, and assigns `p.store` only
once `billy.Open` returns. For that window a lookup hit names a
transaction whose store does not exist yet, and `p.store.Get`
dereferences nil.
Build the cache after `txpool.New` so no reader exists while `Init` runs
-- `getByVhash` has exactly one caller -- and have `getByVhash` read the
store under the same lock as the lookup, returning nil when it is not
set.
A benchmark harness that restarts the client once per test hit this on
1-4% of starts; a node that starts once rarely will.
This commit is contained in:
parent
35fe1bf69c
commit
393555b097
1 changed files with 3 additions and 1 deletions
|
|
@ -329,12 +329,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir)
|
config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir)
|
||||||
}
|
}
|
||||||
eth.blobTxPool = blobpool.New(config.BlobPool, eth.blockchain, legacyPool.HasPendingAuth)
|
eth.blobTxPool = blobpool.New(config.BlobPool, eth.blockchain, legacyPool.HasPendingAuth)
|
||||||
eth.blobCache = blobpool.NewCache(eth.blobTxPool)
|
|
||||||
|
|
||||||
eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, eth.blobTxPool})
|
eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, eth.blobTxPool})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Only after txpool.New has run the pool's Init: the cache reads the pool's
|
||||||
|
// lookup and store, which Init builds without holding the pool lock.
|
||||||
|
eth.blobCache = blobpool.NewCache(eth.blobTxPool)
|
||||||
|
|
||||||
if !config.TxPool.NoLocals {
|
if !config.TxPool.NoLocals {
|
||||||
rejournal := config.TxPool.Rejournal
|
rejournal := config.TxPool.Rejournal
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue