InternPool: prevent anon struct UAF bugs with type safety

Instead of using actual slices for InternPool.Key.AnonStructType, this
commit changes to use Slice types instead, which store a
long-lived index rather than a pointer.

This is a follow-up to 7ef1eb1c27.
This commit is contained in:
Andrew Kelley
2023-09-12 13:32:14 -07:00
parent 7e2b6b0f1b
commit cb6201715a
10 changed files with 248 additions and 178 deletions

View File

@@ -1275,7 +1275,11 @@ pub const DeclGen = struct {
try writer.writeByte('{');
var empty = true;
for (tuple.types, tuple.values, 0..) |field_ty, comptime_ty, field_i| {
for (
tuple.types.get(ip),
tuple.values.get(ip),
0..,
) |field_ty, comptime_ty, field_i| {
if (comptime_ty != .none) continue;
if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) continue;
@@ -7745,16 +7749,18 @@ fn lowerFnRetTy(ret_ty: Type, mod: *Module) !Type {
if (ret_ty.ip_index == .noreturn_type) return Type.noreturn;
if (lowersToArray(ret_ty, mod)) {
const gpa = mod.gpa;
const ip = &mod.intern_pool;
const names = [1]InternPool.NullTerminatedString{
try mod.intern_pool.getOrPutString(mod.gpa, "array"),
try ip.getOrPutString(gpa, "array"),
};
const types = [1]InternPool.Index{ret_ty.ip_index};
const values = [1]InternPool.Index{.none};
const interned = try mod.intern(.{ .anon_struct_type = .{
const interned = try ip.getAnonStructType(gpa, .{
.names = &names,
.types = &types,
.values = &values,
} });
});
return interned.toType();
}

View File

@@ -2392,7 +2392,7 @@ pub const Object = struct {
comptime assert(struct_layout_version == 2);
var offset: u64 = 0;
for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| {
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
const field_size = field_ty.toType().abiSize(mod);
@@ -2401,7 +2401,7 @@ pub const Object = struct {
offset = field_offset + field_size;
const field_name = if (tuple.names.len != 0)
ip.stringToSlice(tuple.names[i])
ip.stringToSlice(tuple.names.get(ip)[i])
else
try std.fmt.allocPrintZ(gpa, "{d}", .{i});
defer if (tuple.names.len == 0) gpa.free(field_name);
@@ -3325,7 +3325,10 @@ pub const Object = struct {
var offset: u64 = 0;
var big_align: u32 = 0;
for (anon_struct_type.types, anon_struct_type.values) |field_ty, field_val| {
for (
anon_struct_type.types.get(ip),
anon_struct_type.values.get(ip),
) |field_ty, field_val| {
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
const field_align = field_ty.toType().abiAlignment(mod);
@@ -3874,7 +3877,11 @@ pub const Object = struct {
var offset: u64 = 0;
var big_align: u32 = 0;
var need_unnamed = false;
for (tuple.types, tuple.values, 0..) |field_ty, field_val, field_index| {
for (
tuple.types.get(ip),
tuple.values.get(ip),
0..,
) |field_ty, field_val, field_index| {
if (field_val != .none) continue;
if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) continue;
@@ -10537,10 +10544,11 @@ fn llvmField(ty: Type, field_index: usize, mod: *Module) ?LlvmField {
var offset: u64 = 0;
var big_align: u32 = 0;
const struct_type = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
const ip = &mod.intern_pool;
const struct_type = switch (ip.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| {
var llvm_field_index: c_uint = 0;
for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| {
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
const field_align = field_ty.toType().abiAlignment(mod);
@@ -11118,6 +11126,7 @@ fn isByRef(ty: Type, mod: *Module) bool {
// For tuples and structs, if there are more than this many non-void
// fields, then we make it byref, otherwise byval.
const max_fields_byval = 0;
const ip = &mod.intern_pool;
switch (ty.zigTypeTag(mod)) {
.Type,
@@ -11146,10 +11155,10 @@ fn isByRef(ty: Type, mod: *Module) bool {
.Struct => {
// Packed structs are represented to LLVM as integers.
if (ty.containerLayout(mod) == .Packed) return false;
const struct_type = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
const struct_type = switch (ip.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| {
var count: usize = 0;
for (tuple.types, tuple.values) |field_ty, field_val| {
for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| {
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
count += 1;

View File

@@ -1227,6 +1227,7 @@ pub const DeclGen = struct {
/// Turn a Zig type into a SPIR-V Type, and return a reference to it.
fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef {
const mod = self.module;
const ip = &mod.intern_pool;
log.debug("resolveType: ty = {}", .{ty.fmt(self.module)});
const target = self.getTarget();
switch (ty.zigTypeTag(mod)) {
@@ -1271,7 +1272,6 @@ pub const DeclGen = struct {
},
.Fn => switch (repr) {
.direct => {
const ip = &mod.intern_pool;
const fn_info = mod.typeToFunc(ty).?;
// TODO: Put this somewhere in Sema.zig
if (fn_info.is_var_args)
@@ -1333,13 +1333,13 @@ pub const DeclGen = struct {
} });
},
.Struct => {
const struct_ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
const struct_ty = switch (ip.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| {
const member_types = try self.gpa.alloc(CacheRef, tuple.values.len);
defer self.gpa.free(member_types);
var member_index: usize = 0;
for (tuple.types, tuple.values) |field_ty, field_val| {
for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| {
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
member_types[member_index] = try self.resolveType(field_ty.toType(), .indirect);
@@ -1369,12 +1369,12 @@ pub const DeclGen = struct {
while (it.next()) |field_and_index| {
const field = field_and_index.field;
const index = field_and_index.index;
const field_name = mod.intern_pool.stringToSlice(struct_obj.fields.keys()[index]);
const field_name = ip.stringToSlice(struct_obj.fields.keys()[index]);
try member_types.append(try self.resolveType(field.ty, .indirect));
try member_names.append(try self.spv.resolveString(field_name));
}
const name = mod.intern_pool.stringToSlice(try struct_obj.getFullyQualifiedName(self.module));
const name = ip.stringToSlice(try struct_obj.getFullyQualifiedName(self.module));
return try self.spv.resolve(.{ .struct_type = .{
.name = try self.spv.resolveString(name),