mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
core/types/bal: deep-copy borrowed storage map in Merge
When Merge encountered an address that was absent from the receiver it assigned the source's inner StorageAccessList map by reference, so both lists ended up sharing the same map. Any subsequent AddState on either side would leak into the other. Clone the borrowed map on adoption to keep the two lists independent, mirroring the deep-copy discipline used by accessList, accessEvents and the newly added StateAccessList.Copy. The method currently has no production caller but is part of the public surface being prepared for BAL construction, so trap the bug before it wires up. Add a small regression test that fails on the previous code.
This commit is contained in:
parent
b70d9a4b8e
commit
88866b2a4c
2 changed files with 27 additions and 1 deletions
|
|
@ -72,7 +72,9 @@ func (s *StateAccessList) Merge(other *StateAccessList) {
|
|||
for addr, otherSlots := range other.list {
|
||||
slots, exists := s.list[addr]
|
||||
if !exists {
|
||||
s.list[addr] = otherSlots
|
||||
// Clone the borrowed map so later mutations on either list
|
||||
// don't leak into the other.
|
||||
s.list[addr] = maps.Clone(otherSlots)
|
||||
continue
|
||||
}
|
||||
maps.Copy(slots, otherSlots)
|
||||
|
|
|
|||
|
|
@ -252,3 +252,27 @@ func TestBlockAccessListValidation(t *testing.T) {
|
|||
t.Fatalf("Unexpected validation error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStateAccessListMergeIsolation verifies that StateAccessList.Merge does
|
||||
// not alias the source's inner storage map into the receiver. Regression test
|
||||
// for a bug where mutations to a merged list leaked back into the source.
|
||||
func TestStateAccessListMergeIsolation(t *testing.T) {
|
||||
addr := common.BytesToAddress([]byte{0x01})
|
||||
slotA := common.BytesToHash([]byte{0x0A})
|
||||
slotB := common.BytesToHash([]byte{0x0B})
|
||||
|
||||
src := NewStateAccessList()
|
||||
src.AddState(addr, slotA)
|
||||
|
||||
dst := NewStateAccessList()
|
||||
dst.Merge(src)
|
||||
|
||||
// Mutate only the receiver. Before the fix this also mutated src because
|
||||
// Merge assigned src's inner map by reference when the receiver lacked
|
||||
// the address.
|
||||
dst.AddState(addr, slotB)
|
||||
|
||||
if _, leaked := src.list[addr][slotB]; leaked {
|
||||
t.Fatalf("Merge aliased source map: slotB leaked into src after dst-only mutation")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue