diff --git a/core/state/stateupdate.go b/core/state/stateupdate.go index 44df4427df..342a80b73f 100644 --- a/core/state/stateupdate.go +++ b/core/state/stateupdate.go @@ -301,10 +301,18 @@ func (sc *stateUpdate) encodeBinary() (map[common.Hash][]byte, map[common.Addres accounts[fullKey] = nil continue } + // Defensive length check: every non-nil bintrie leaf must be + // exactly 32 bytes. A wrong-length leaf from the hasher would + // silently produce garbage in the diff layer; catch it here at + // the trust boundary rather than deep in the flush path where + // the stemBuilder.set panic would fire with less context. + if len(w.Value) != 32 { + return nil, nil, nil, nil, fmt.Errorf("bintrie leaf at stem %x offset %d has value len %d, want 32", w.Stem, w.Offset, len(w.Value)) + } // Take an owning copy: the hasher reuses its underlying buffers // across blocks, so retaining its slices would create cross-block // aliasing bugs in the pathdb diff layer. - v := make([]byte, len(w.Value)) + v := make([]byte, 32) copy(v, w.Value) accounts[fullKey] = v } diff --git a/triedb/pathdb/stem_blob.go b/triedb/pathdb/stem_blob.go index fce8be98ae..5ec731b95f 100644 --- a/triedb/pathdb/stem_blob.go +++ b/triedb/pathdb/stem_blob.go @@ -84,6 +84,9 @@ func encodeStemBlob(bitmap [stemBlobBitmapSize]byte, values [][]byte) ([]byte, e if count != len(values) { return nil, fmt.Errorf("stem blob popcount=%d values=%d: %w", count, len(values), errStemBlobMalformed) } + if count > stemBlobBitmapBits { + return nil, fmt.Errorf("stem blob value count %d exceeds max %d: %w", count, stemBlobBitmapBits, errStemBlobMalformed) + } if count == 0 { return nil, nil }