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) {...@@ -721,6 +721,9 @@ pub const Tag = enum(u8) {
721 /// only an enum tag, but will be presented via the API with a different Key.721 /// only an enum tag, but will be presented via the API with a different Key.
722 /// data is SimpleInternal enum value.722 /// data is SimpleInternal enum value.
723 simple_internal,723 simple_internal,
724 /// Type: u16
725 /// data is integer value
726 int_u16,
724 /// Type: u32727 /// Type: u32
725 /// data is integer value728 /// data is integer value
726 int_u32,729 int_u32,
...@@ -1053,6 +1056,10 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {...@@ -1053,6 +1056,10 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
1053 .type_error_union => @panic("TODO"),1056 .type_error_union => @panic("TODO"),
1054 .type_enum_simple => @panic("TODO"),1057 .type_enum_simple => @panic("TODO"),
1055 .simple_internal => @panic("TODO"),1058 .simple_internal => @panic("TODO"),
1059 .int_u16 => .{ .int = .{
1060 .ty = .u16_type,
1061 .storage = .{ .u64 = data },
1062 } },
1056 .int_u32 => .{ .int = .{1063 .int_u32 => .{ .int = .{
1057 .ty = .u32_type,1064 .ty = .u32_type,
1058 .storage = .{ .u64 = data },1065 .storage = .{ .u64 = data },
...@@ -1219,6 +1226,26 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {...@@ -1219,6 +1226,26 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
1219 .int => |int| b: {1226 .int => |int| b: {
1220 switch (int.ty) {1227 switch (int.ty) {
1221 .none => unreachable,1228 .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 },
1222 .u32_type => switch (int.storage) {1249 .u32_type => switch (int.storage) {
1223 .big_int => |big_int| {1250 .big_int => |big_int| {
1224 if (big_int.to(u32)) |casted| {1251 if (big_int.to(u32)) |casted| {
...@@ -1252,7 +1279,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {...@@ -1252,7 +1279,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
1252 inline .u64, .i64 => |x| {1279 inline .u64, .i64 => |x| {
1253 if (std.math.cast(i32, x)) |casted| {1280 if (std.math.cast(i32, x)) |casted| {
1254 ip.items.appendAssumeCapacity(.{1281 ip.items.appendAssumeCapacity(.{
1255 .tag = .int_u32,1282 .tag = .int_i32,
1256 .data = @bitCast(u32, casted),1283 .data = @bitCast(u32, casted),
1257 });1284 });
1258 break :b;1285 break :b;
...@@ -1466,6 +1493,7 @@ fn limbData(ip: InternPool, comptime T: type, index: usize) T {...@@ -1466,6 +1493,7 @@ fn limbData(ip: InternPool, comptime T: type, index: usize) T {
1466 return result;1493 return result;
1467}1494}
14681495
1496/// This function returns the Limb slice that is trailing data after a payload.
1469fn limbSlice(ip: InternPool, comptime S: type, limb_index: u32, len: u32) []const Limb {1497fn limbSlice(ip: InternPool, comptime S: type, limb_index: u32, len: u32) []const Limb {
1470 const field_count = @typeInfo(S).Struct.fields.len;1498 const field_count = @typeInfo(S).Struct.fields.len;
1471 switch (@sizeOf(Limb)) {1499 switch (@sizeOf(Limb)) {
...@@ -1481,6 +1509,33 @@ fn limbSlice(ip: InternPool, comptime S: type, limb_index: u32, len: u32) []cons...@@ -1481,6 +1509,33 @@ fn limbSlice(ip: InternPool, comptime S: type, limb_index: u32, len: u32) []cons
1481 }1509 }
1482}1510}
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
1484test "basic usage" {1539test "basic usage" {
1485 const gpa = std.testing.allocator;1540 const gpa = std.testing.allocator;
14861541
...@@ -1544,15 +1599,30 @@ pub fn getCoercedInt(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index)...@@ -1544,15 +1599,30 @@ pub fn getCoercedInt(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index)
1544 // Here we pre-reserve the limbs to ensure that the logic in `addInt` will1599 // Here we pre-reserve the limbs to ensure that the logic in `addInt` will
1545 // not use an invalidated limbs pointer.1600 // not use an invalidated limbs pointer.
1546 switch (key.int.storage) {1601 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
1548 .big_int => |big_int| {1611 .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.
1549 try reserveLimbs(ip, gpa, @typeInfo(Int).Struct.fields.len + big_int.limbs.len);1616 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 } });
1550 },1624 },
1551 }1625 }
1552 return ip.get(gpa, .{ .int = .{
1553 .ty = new_ty,
1554 .storage = key.int.storage,
1555 } });
1556}1626}
15571627
1558pub fn dump(ip: InternPool) void {1628pub fn dump(ip: InternPool) void {
...@@ -1608,6 +1678,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {...@@ -1608,6 +1678,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
1608 .simple_type => 0,1678 .simple_type => 0,
1609 .simple_value => 0,1679 .simple_value => 0,
1610 .simple_internal => 0,1680 .simple_internal => 0,
1681 .int_u16 => 0,
1611 .int_u32 => 0,1682 .int_u32 => 0,
1612 .int_i32 => 0,1683 .int_i32 => 0,
1613 .int_usize => 0,1684 .int_usize => 0,