authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-07 15:06:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-10 20:42:29-07:00
log8699cdc3dfcf3a3a6f09a64ea9c67be2459e1240
tree78d4142325d385513a4691e27813fba6d1f3d5dc
parent2ffef605c75b62ba49e21bfb3256537a4a2c0a5e

InternPool: fix UAF in getCoercedInt and add u16 int value encoding


1 files changed, 77 insertions(+), 6 deletions(-)

src/InternPool.zig+77-6
......@@ -721,6 +721,9 @@ pub const Tag = enum(u8) {
721721 /// only an enum tag, but will be presented via the API with a different Key.
722722 /// data is SimpleInternal enum value.
723723 simple_internal,
724 /// Type: u16
725 /// data is integer value
726 int_u16,
724727 /// Type: u32
725728 /// data is integer value
726729 int_u32,
......@@ -1053,6 +1056,10 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
10531056 .type_error_union => @panic("TODO"),
10541057 .type_enum_simple => @panic("TODO"),
10551058 .simple_internal => @panic("TODO"),
1059 .int_u16 => .{ .int = .{
1060 .ty = .u16_type,
1061 .storage = .{ .u64 = data },
1062 } },
10561063 .int_u32 => .{ .int = .{
10571064 .ty = .u32_type,
10581065 .storage = .{ .u64 = data },
......@@ -1219,6 +1226,26 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
12191226 .int => |int| b: {
12201227 switch (int.ty) {
12211228 .none => unreachable,
1229 .u16_type => switch (int.storage) {
1230 .big_int => |big_int| {
1231 if (big_int.to(u32)) |casted| {
1232 ip.items.appendAssumeCapacity(.{
1233 .tag = .int_u16,
1234 .data = casted,
1235 });
1236 break :b;
1237 } else |_| {}
1238 },
1239 inline .u64, .i64 => |x| {
1240 if (std.math.cast(u32, x)) |casted| {
1241 ip.items.appendAssumeCapacity(.{
1242 .tag = .int_u16,
1243 .data = casted,
1244 });
1245 break :b;
1246 }
1247 },
1248 },
12221249 .u32_type => switch (int.storage) {
12231250 .big_int => |big_int| {
12241251 if (big_int.to(u32)) |casted| {
......@@ -1252,7 +1279,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
12521279 inline .u64, .i64 => |x| {
12531280 if (std.math.cast(i32, x)) |casted| {
12541281 ip.items.appendAssumeCapacity(.{
1255 .tag = .int_u32,
1282 .tag = .int_i32,
12561283 .data = @bitCast(u32, casted),
12571284 });
12581285 break :b;
......@@ -1466,6 +1493,7 @@ fn limbData(ip: InternPool, comptime T: type, index: usize) T {
14661493 return result;
14671494}
14681495
1496/// This function returns the Limb slice that is trailing data after a payload.
14691497fn limbSlice(ip: InternPool, comptime S: type, limb_index: u32, len: u32) []const Limb {
14701498 const field_count = @typeInfo(S).Struct.fields.len;
14711499 switch (@sizeOf(Limb)) {
......@@ -1481,6 +1509,33 @@ fn limbSlice(ip: InternPool, comptime S: type, limb_index: u32, len: u32) []cons
14811509 }
14821510}
14831511
1512const LimbsAsIndexes = struct {
1513 start: u32,
1514 len: u32,
1515};
1516
1517fn limbsSliceToIndex(ip: InternPool, limbs: []const Limb) LimbsAsIndexes {
1518 const host_slice = switch (@sizeOf(Limb)) {
1519 @sizeOf(u32) => ip.extra.items,
1520 @sizeOf(u64) => ip.limbs.items,
1521 else => @compileError("unsupported host"),
1522 };
1523 // TODO: https://github.com/ziglang/zig/issues/1738
1524 return .{
1525 .start = @intCast(u32, @divExact(@ptrToInt(limbs.ptr) - @ptrToInt(host_slice.ptr), @sizeOf(Limb))),
1526 .len = @intCast(u32, limbs.len),
1527 };
1528}
1529
1530/// This function converts Limb array indexes to a primitive slice type.
1531fn limbsIndexToSlice(ip: InternPool, limbs: LimbsAsIndexes) []const Limb {
1532 return switch (@sizeOf(Limb)) {
1533 @sizeOf(u32) => ip.extra.items[limbs.start..][0..limbs.len],
1534 @sizeOf(u64) => ip.limbs.items[limbs.start..][0..limbs.len],
1535 else => @compileError("unsupported host"),
1536 };
1537}
1538
14841539test "basic usage" {
14851540 const gpa = std.testing.allocator;
14861541
......@@ -1544,15 +1599,30 @@ pub fn getCoercedInt(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index)
15441599 // Here we pre-reserve the limbs to ensure that the logic in `addInt` will
15451600 // not use an invalidated limbs pointer.
15461601 switch (key.int.storage) {
1547 .u64, .i64 => {},
1602 .u64 => |x| return ip.get(gpa, .{ .int = .{
1603 .ty = new_ty,
1604 .storage = .{ .u64 = x },
1605 } }),
1606 .i64 => |x| return ip.get(gpa, .{ .int = .{
1607 .ty = new_ty,
1608 .storage = .{ .i64 = x },
1609 } }),
1610
15481611 .big_int => |big_int| {
1612 const positive = big_int.positive;
1613 const limbs = ip.limbsSliceToIndex(big_int.limbs);
1614 // This line invalidates the limbs slice, but the indexes computed in the
1615 // previous line are still correct.
15491616 try reserveLimbs(ip, gpa, @typeInfo(Int).Struct.fields.len + big_int.limbs.len);
1617 return ip.get(gpa, .{ .int = .{
1618 .ty = new_ty,
1619 .storage = .{ .big_int = .{
1620 .limbs = ip.limbsIndexToSlice(limbs),
1621 .positive = positive,
1622 } },
1623 } });
15501624 },
15511625 }
1552 return ip.get(gpa, .{ .int = .{
1553 .ty = new_ty,
1554 .storage = key.int.storage,
1555 } });
15561626}
15571627
15581628pub fn dump(ip: InternPool) void {
......@@ -1608,6 +1678,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
16081678 .simple_type => 0,
16091679 .simple_value => 0,
16101680 .simple_internal => 0,
1681 .int_u16 => 0,
16111682 .int_u32 => 0,
16121683 .int_i32 => 0,
16131684 .int_usize => 0,