This commit is contained in:
ozpool 2026-07-17 21:52:54 -07:00 committed by GitHub
commit 8124678cfa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 29 additions and 5 deletions

View file

@ -397,20 +397,25 @@ func decodeRestartTrailer(keySection []byte) ([]uint32, []uint32, int, error) {
} }
nRestarts := binary.BigEndian.Uint32(keySection[len(keySection)-4:]) 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 ( var (
keyOffsets = make([]uint32, 0, int(nRestarts)) keyOffsets = make([]uint32, 0, int(nRestarts))
valOffsets = 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) { for i := range int(nRestarts) {
o := len(keySection) - 4 - (int(nRestarts)-i)*8 o := len(keySection) - 4 - (int(nRestarts)-i)*8
keyOffset := binary.BigEndian.Uint32(keySection[o : o+4]) keyOffset := binary.BigEndian.Uint32(keySection[o : o+4])
if i != 0 && keyOffset <= keyOffsets[i-1] { 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) 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) keyOffsets = append(keyOffsets, keyOffset)
// Same value offset is allowed just in case all the trie nodes in the last // 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) valOffsets = append(valOffsets, valOffset)
} }
keyLimit := len(keySection) - 4 - int(nRestarts)*8 // End of key data
return keyOffsets, valOffsets, keyLimit, nil return keyOffsets, valOffsets, keyLimit, nil
} }

View file

@ -656,6 +656,26 @@ func TestDecodeSingleCorruptedData(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("Expected error for invalid restart count") 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 // Helper function to test encode/decode cycle