diff --git a/triedb/pathdb/history_trienode.go b/triedb/pathdb/history_trienode.go index 11d6112806..27ced3aa70 100644 --- a/triedb/pathdb/history_trienode.go +++ b/triedb/pathdb/history_trienode.go @@ -397,20 +397,25 @@ func decodeRestartTrailer(keySection []byte) ([]uint32, []uint32, int, error) { } nRestarts := binary.BigEndian.Uint32(keySection[len(keySection)-4:]) - // Decode the trailer + // Decode the trailer. The multiplication is performed in 64-bit to prevent + // the result from wrapping around for large restart counts. + if uint64(len(keySection)) < uint64(nRestarts)*8+4 { + return nil, nil, 0, fmt.Errorf("key section too short, restarts: %d, size: %d", nRestarts, len(keySection)) + } var ( keyOffsets = make([]uint32, 0, int(nRestarts)) valOffsets = make([]uint32, 0, int(nRestarts)) + keyLimit = len(keySection) - 4 - int(nRestarts)*8 // End of key data ) - if len(keySection) < int(8*nRestarts)+4 { - return nil, nil, 0, fmt.Errorf("key section too short, restarts: %d, size: %d", nRestarts, len(keySection)) - } for i := range int(nRestarts) { o := len(keySection) - 4 - (int(nRestarts)-i)*8 keyOffset := binary.BigEndian.Uint32(keySection[o : o+4]) if i != 0 && keyOffset <= keyOffsets[i-1] { return nil, nil, 0, fmt.Errorf("key offset is out of order, prev: %v, cur: %v", keyOffsets[i-1], keyOffset) } + if int(keyOffset) > keyLimit { + return nil, nil, 0, fmt.Errorf("key offset is out of range, offset: %d, limit: %d", keyOffset, keyLimit) + } keyOffsets = append(keyOffsets, keyOffset) // Same value offset is allowed just in case all the trie nodes in the last @@ -421,7 +426,6 @@ func decodeRestartTrailer(keySection []byte) ([]uint32, []uint32, int, error) { } valOffsets = append(valOffsets, valOffset) } - keyLimit := len(keySection) - 4 - int(nRestarts)*8 // End of key data return keyOffsets, valOffsets, keyLimit, nil } diff --git a/triedb/pathdb/history_trienode_test.go b/triedb/pathdb/history_trienode_test.go index 05278c30b1..aab79f4174 100644 --- a/triedb/pathdb/history_trienode_test.go +++ b/triedb/pathdb/history_trienode_test.go @@ -656,6 +656,26 @@ func TestDecodeSingleCorruptedData(t *testing.T) { if err == nil { t.Fatal("Expected error for invalid restart count") } + + // Test with a restart count large enough to overflow the trailer size + // computation (8*nRestarts wraps around for nRestarts >= 1<<29). + corrupted = make([]byte, len(keySection)) + copy(corrupted, keySection) + binary.BigEndian.PutUint32(corrupted[len(corrupted)-4:], 1<<29) + err = decodeSingle(corrupted, nil) + if err == nil { + t.Fatal("Expected error for overflowing restart count") + } + + // Test with a key offset pointing beyond the end of the key data + corrupted = make([]byte, 12) + binary.BigEndian.PutUint32(corrupted[0:4], 0xffffffff) // key offset + binary.BigEndian.PutUint32(corrupted[4:8], 0) // value offset + binary.BigEndian.PutUint32(corrupted[8:12], 1) // restart count + err = decodeSingle(corrupted, nil) + if err == nil { + t.Fatal("Expected error for out-of-range key offset") + } } // Helper function to test encode/decode cycle