fix(terminal): prevent integer overflow in hash_map layoutForCapacity
Co-Authored-By: Sachin <sachinbeniwal0101@gmail.com>pull/9871/head
parent
bed219c132
commit
0d8c193bda
|
|
@ -856,13 +856,17 @@ fn HashMapUnmanaged(
|
|||
pub fn layoutForCapacity(new_capacity: Size) Layout {
|
||||
assert(new_capacity == 0 or std.math.isPowerOfTwo(new_capacity));
|
||||
|
||||
// Cast to usize to prevent overflow in size calculations.
|
||||
// See: https://github.com/ziglang/zig/pull/19048
|
||||
const cap: usize = new_capacity;
|
||||
|
||||
// Pack our metadata, keys, and values.
|
||||
const meta_start = @sizeOf(Header);
|
||||
const meta_end = @sizeOf(Header) + new_capacity * @sizeOf(Metadata);
|
||||
const meta_end = @sizeOf(Header) + cap * @sizeOf(Metadata);
|
||||
const keys_start = std.mem.alignForward(usize, meta_end, key_align);
|
||||
const keys_end = keys_start + new_capacity * @sizeOf(K);
|
||||
const keys_end = keys_start + cap * @sizeOf(K);
|
||||
const vals_start = std.mem.alignForward(usize, keys_end, val_align);
|
||||
const vals_end = vals_start + new_capacity * @sizeOf(V);
|
||||
const vals_end = vals_start + cap * @sizeOf(V);
|
||||
|
||||
// Our total memory size required is the end of our values
|
||||
// aligned to the base required alignment.
|
||||
|
|
@ -1512,3 +1516,26 @@ test "OffsetHashMap remake map" {
|
|||
try expectEqual(5, map.get(5).?);
|
||||
}
|
||||
}
|
||||
|
||||
test "layoutForCapacity no overflow for large capacity" {
|
||||
// Test that layoutForCapacity correctly handles large capacities without overflow.
|
||||
// Prior to the fix, new_capacity (u32) was multiplied before widening to usize,
|
||||
// causing overflow when new_capacity * @sizeOf(K) exceeded 2^32.
|
||||
// See: https://github.com/ghostty-org/ghostty/issues/9862
|
||||
const Map = AutoHashMapUnmanaged(u64, u64);
|
||||
|
||||
// Use 2^30 capacity - this would overflow in u32 when multiplied by @sizeOf(u64)=8
|
||||
// 0x40000000 * 8 = 0x2_0000_0000 which wraps to 0 in u32
|
||||
const large_cap: Map.Size = 1 << 30;
|
||||
const layout = Map.layoutForCapacity(large_cap);
|
||||
|
||||
// With the fix, total_size should be at least cap * (sizeof(K) + sizeof(V))
|
||||
// = 2^30 * 16 = 2^34 bytes = 16 GiB
|
||||
// Without the fix, this would wrap and produce a much smaller value.
|
||||
const min_expected: usize = @as(usize, large_cap) * (@sizeOf(u64) + @sizeOf(u64));
|
||||
try expect(layout.total_size >= min_expected);
|
||||
|
||||
// Also verify the individual offsets don't wrap
|
||||
try expect(layout.keys_start > 0);
|
||||
try expect(layout.vals_start > layout.keys_start);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue