aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-09-12 13:32:14 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-09-12 20:08:56 -0400
commitcb6201715a7bcae2b278811186afc17a697b25f7 (patch)
tree30db7b0844d0769b87664e83164f734fae1a9627 /src/Sema.zig
parent7e2b6b0f1bc5877f11c50a217dd88c11481bbad4 (diff)
downloadzig-cb6201715a7bcae2b278811186afc17a697b25f7.tar.gz
zig-cb6201715a7bcae2b278811186afc17a697b25f7.zip
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 7ef1eb1c27754cb0349fdc10db1f02ff2dddd99b.
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig111
1 files changed, 60 insertions, 51 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index f7df6b1270..a7a9e99840 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -8052,11 +8052,12 @@ fn instantiateGenericCall(
fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
const mod = sema.mod;
- const tuple = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
+ const ip = &mod.intern_pool;
+ const tuple = switch (ip.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| tuple,
else => return,
};
- for (tuple.types, tuple.values) |field_ty, field_val| {
+ for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| {
try sema.resolveTupleLazyValues(block, src, field_ty.toType());
if (field_val == .none) continue;
// TODO: mutate in intern pool
@@ -12929,7 +12930,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
},
.anon_struct_type => |anon_struct| {
if (anon_struct.names.len != 0) {
- break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names, field_name) != null;
+ break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names.get(ip), field_name) != null;
} else {
const field_index = field_name.toUnsigned(ip) orelse break :hf false;
break :hf field_index < ty.structFieldCount(mod);
@@ -13558,11 +13559,11 @@ fn analyzeTupleCat(
break :rs runtime_src;
};
- const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
+ const tuple_ty = try mod.intern_pool.getAnonStructType(mod.gpa, .{
.types = types,
.values = values,
.names = &.{},
- } });
+ });
const runtime_src = opt_runtime_src orelse {
const tuple_val = try mod.intern(.{ .aggregate = .{
@@ -13889,11 +13890,11 @@ fn analyzeTupleMul(
break :rs runtime_src;
};
- const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
+ const tuple_ty = try mod.intern_pool.getAnonStructType(mod.gpa, .{
.types = types,
.values = values,
.names = &.{},
- } });
+ });
const runtime_src = opt_runtime_src orelse {
const tuple_val = try mod.intern(.{ .aggregate = .{
@@ -15217,6 +15218,7 @@ fn zirOverflowArithmetic(
const lhs_ty = sema.typeOf(uncasted_lhs);
const rhs_ty = sema.typeOf(uncasted_rhs);
const mod = sema.mod;
+ const ip = &mod.intern_pool;
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
@@ -15244,7 +15246,7 @@ fn zirOverflowArithmetic(
const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs);
const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
- const overflow_ty = mod.intern_pool.indexToKey(tuple_ty.toIntern()).anon_struct_type.types[1].toType();
+ const overflow_ty = ip.indexToKey(tuple_ty.toIntern()).anon_struct_type.types.get(ip)[1].toType();
var result: struct {
inst: Air.Inst.Ref = .none,
@@ -15418,6 +15420,7 @@ fn splat(sema: *Sema, ty: Type, val: Value) !Value {
fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
const mod = sema.mod;
+ const ip = &mod.intern_pool;
const ov_ty = if (ty.zigTypeTag(mod) == .Vector) try mod.vectorType(.{
.len = ty.vectorLen(mod),
.child = .u1_type,
@@ -15425,11 +15428,11 @@ fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
const types = [2]InternPool.Index{ ty.toIntern(), ov_ty.toIntern() };
const values = [2]InternPool.Index{ .none, .none };
- const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
+ const tuple_ty = try ip.getAnonStructType(mod.gpa, .{
.types = &types,
.values = &values,
.names = &.{},
- } });
+ });
return tuple_ty.toType();
}
@@ -17578,15 +17581,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len);
for (struct_field_vals, 0..) |*struct_field_val, i| {
const anon_struct_type = ip.indexToKey(ty.toIntern()).anon_struct_type;
- const field_ty = anon_struct_type.types[i];
- const field_val = anon_struct_type.values[i];
+ const field_ty = anon_struct_type.types.get(ip)[i];
+ const field_val = anon_struct_type.values.get(ip)[i];
const name_val = v: {
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
// TODO: write something like getCoercedInts to avoid needing to dupe
const bytes = if (tuple.names.len != 0)
// https://github.com/ziglang/zig/issues/15709
- try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(ty.toIntern()).anon_struct_type.names[i]))
+ try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip)[i]))
else
try std.fmt.allocPrint(sema.arena, "{d}", .{i});
const new_decl_ty = try mod.arrayType(.{
@@ -19254,7 +19257,7 @@ fn finishStructInit(
switch (ip.indexToKey(struct_ty.toIntern())) {
.anon_struct_type => |anon_struct| {
- for (anon_struct.values, 0..) |default_val, i| {
+ for (anon_struct.values.get(ip), 0..) |default_val, i| {
if (field_inits[i] != .none) continue;
if (default_val == .none) {
@@ -19266,7 +19269,7 @@ fn finishStructInit(
root_msg = try sema.errMsg(block, init_src, template, .{i});
}
} else {
- const field_name = anon_struct.names[i];
+ const field_name = anon_struct.names.get(ip)[i];
const template = "missing struct field: {}";
const args = .{field_name.fmt(ip)};
if (root_msg) |msg| {
@@ -19395,6 +19398,7 @@ fn structInitAnon(
) CompileError!Air.Inst.Ref {
const mod = sema.mod;
const gpa = sema.gpa;
+ const ip = &mod.intern_pool;
const zir_datas = sema.code.instructions.items(.data);
const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
@@ -19465,11 +19469,11 @@ fn structInitAnon(
break :rs runtime_index;
};
- const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
+ const tuple_ty = try ip.getAnonStructType(gpa, .{
.names = fields.keys(),
.types = types,
.values = values,
- } });
+ });
const runtime_index = opt_runtime_index orelse {
const tuple_val = try mod.intern(.{ .aggregate = .{
@@ -19688,6 +19692,8 @@ fn arrayInitAnon(
is_ref: bool,
) CompileError!Air.Inst.Ref {
const mod = sema.mod;
+ const gpa = sema.gpa;
+ const ip = &mod.intern_pool;
const types = try sema.arena.alloc(InternPool.Index, operands.len);
const values = try sema.arena.alloc(InternPool.Index, operands.len);
@@ -19701,7 +19707,7 @@ fn arrayInitAnon(
if (types[i].toType().zigTypeTag(mod) == .Opaque) {
const msg = msg: {
const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
- errdefer msg.destroy(sema.gpa);
+ errdefer msg.destroy(gpa);
try sema.addDeclaredHereNote(msg, types[i].toType());
break :msg msg;
@@ -19718,11 +19724,11 @@ fn arrayInitAnon(
break :rs runtime_src;
};
- const tuple_ty = try mod.intern(.{ .anon_struct_type = .{
+ const tuple_ty = try ip.getAnonStructType(gpa, .{
.types = types,
.values = values,
.names = &.{},
- } });
+ });
const runtime_src = opt_runtime_src orelse {
const tuple_val = try mod.intern(.{ .aggregate = .{
@@ -19832,7 +19838,7 @@ fn fieldType(
.Struct => switch (ip.indexToKey(cur_ty.toIntern())) {
.anon_struct_type => |anon_struct| {
const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src);
- return Air.internedToRef(anon_struct.types[field_index]);
+ return Air.internedToRef(anon_struct.types.get(ip)[field_index]);
},
.struct_type => |struct_type| {
const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
@@ -30574,13 +30580,14 @@ fn coerceAnonStructToUnion(
inst_src: LazySrcLoc,
) !Air.Inst.Ref {
const mod = sema.mod;
+ const ip = &mod.intern_pool;
const inst_ty = sema.typeOf(inst);
const field_info: union(enum) {
name: InternPool.NullTerminatedString,
count: usize,
- } = switch (mod.intern_pool.indexToKey(inst_ty.toIntern())) {
+ } = switch (ip.indexToKey(inst_ty.toIntern())) {
.anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1)
- .{ .name = anon_struct_type.names[0] }
+ .{ .name = anon_struct_type.names.get(ip)[0] }
else
.{ .count = anon_struct_type.names.len },
.struct_type => |struct_type| name: {
@@ -30876,7 +30883,7 @@ fn coerceTupleToStruct(
// https://github.com/ziglang/zig/issues/15709
const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
.anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
- anon_struct_type.names[field_i]
+ anon_struct_type.names.get(ip)[field_i]
else
try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
.struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i],
@@ -30994,7 +31001,7 @@ fn coerceTupleToTuple(
// https://github.com/ziglang/zig/issues/15709
const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) {
.anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0)
- anon_struct_type.names[field_i]
+ anon_struct_type.names.get(ip)[field_i]
else
try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}),
.struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.keys()[field_i],
@@ -31005,12 +31012,12 @@ fn coerceTupleToTuple(
return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) {
- .anon_struct_type => |anon_struct_type| anon_struct_type.types[field_index_usize].toType(),
+ .anon_struct_type => |anon_struct_type| anon_struct_type.types.get(ip)[field_index_usize].toType(),
.struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].ty,
else => unreachable,
};
const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
- .anon_struct_type => |anon_struct_type| anon_struct_type.values[field_index_usize],
+ .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[field_index_usize],
.struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[field_index_usize].default_val,
else => unreachable,
};
@@ -31048,7 +31055,7 @@ fn coerceTupleToTuple(
if (field_ref.* != .none) continue;
const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
- .anon_struct_type => |anon_struct_type| anon_struct_type.values[i],
+ .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[i],
.struct_type => |struct_type| mod.structPtrUnwrap(struct_type.index).?.fields.values()[i].default_val,
else => unreachable,
};
@@ -32855,6 +32862,7 @@ fn resolvePeerTypesInner(
peer_vals: []?Value,
) !PeerResolveResult {
const mod = sema.mod;
+ const ip = &mod.intern_pool;
var strat_reason: usize = 0;
var s: PeerResolveStrategy = .unknown;
@@ -32912,7 +32920,7 @@ fn resolvePeerTypesInner(
.ErrorUnion => blk: {
const set_ty = ty.errorUnionSet(mod);
ty_ptr.* = ty.errorUnionPayload(mod);
- if (val_ptr.*) |eu_val| switch (mod.intern_pool.indexToKey(eu_val.toIntern())) {
+ if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) {
.error_union => |eu| switch (eu.val) {
.payload => |payload_ip| val_ptr.* = payload_ip.toValue(),
.err_name => val_ptr.* = null,
@@ -33166,8 +33174,8 @@ fn resolvePeerTypesInner(
}).toIntern();
if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
- const peer_sent = try mod.intern_pool.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child);
- const ptr_sent = try mod.intern_pool.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child);
+ const peer_sent = try ip.getCoerced(sema.gpa, ptr_info.sentinel, ptr_info.child);
+ const ptr_sent = try ip.getCoerced(sema.gpa, peer_info.sentinel, ptr_info.child);
if (ptr_sent == peer_sent) {
ptr_info.sentinel = ptr_sent;
} else {
@@ -33278,7 +33286,7 @@ fn resolvePeerTypesInner(
ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) {
- .One => switch (mod.intern_pool.indexToKey(peer_info.child)) {
+ .One => switch (ip.indexToKey(peer_info.child)) {
.array_type => |array_type| array_type.sentinel,
else => .none,
},
@@ -33287,7 +33295,7 @@ fn resolvePeerTypesInner(
};
const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) {
- .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) {
+ .One => switch (ip.indexToKey(ptr_info.child)) {
.array_type => |array_type| array_type.sentinel,
else => .none,
},
@@ -33449,7 +33457,7 @@ fn resolvePeerTypesInner(
}
const sentinel_ty = switch (ptr_info.flags.size) {
- .One => switch (mod.intern_pool.indexToKey(ptr_info.child)) {
+ .One => switch (ip.indexToKey(ptr_info.child)) {
.array_type => |array_type| array_type.child,
else => ptr_info.child,
},
@@ -33460,11 +33468,11 @@ fn resolvePeerTypesInner(
no_sentinel: {
if (peer_sentinel == .none) break :no_sentinel;
if (cur_sentinel == .none) break :no_sentinel;
- const peer_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, peer_sentinel, sentinel_ty);
- const cur_sent_coerced = try mod.intern_pool.getCoerced(sema.gpa, cur_sentinel, sentinel_ty);
+ const peer_sent_coerced = try ip.getCoerced(sema.gpa, peer_sentinel, sentinel_ty);
+ const cur_sent_coerced = try ip.getCoerced(sema.gpa, cur_sentinel, sentinel_ty);
if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
// Sentinels match
- if (ptr_info.flags.size == .One) switch (mod.intern_pool.indexToKey(ptr_info.child)) {
+ if (ptr_info.flags.size == .One) switch (ip.indexToKey(ptr_info.child)) {
.array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
.len = array_type.len,
.child = array_type.child,
@@ -33478,7 +33486,7 @@ fn resolvePeerTypesInner(
}
// Clear existing sentinel
ptr_info.sentinel = .none;
- switch (mod.intern_pool.indexToKey(ptr_info.child)) {
+ switch (ip.indexToKey(ptr_info.child)) {
.array_type => |array_type| ptr_info.child = (try mod.arrayType(.{
.len = array_type.len,
.child = array_type.child,
@@ -33501,7 +33509,7 @@ fn resolvePeerTypesInner(
.peer_idx_a = first_idx,
.peer_idx_b = other_idx,
} },
- else => switch (mod.intern_pool.indexToKey(pointee)) {
+ else => switch (ip.indexToKey(pointee)) {
.array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{
.peer_idx_a = first_idx,
.peer_idx_b = other_idx,
@@ -33785,7 +33793,7 @@ fn resolvePeerTypesInner(
is_tuple = ty.isTuple(mod);
field_count = ty.structFieldCount(mod);
if (!is_tuple) {
- const names = mod.intern_pool.indexToKey(ty.toIntern()).anon_struct_type.names;
+ const names = ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip);
field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names);
}
continue;
@@ -33839,7 +33847,7 @@ fn resolvePeerTypesInner(
result_buf.* = result;
const field_name = if (is_tuple) name: {
break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx});
- } else try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(field_names[field_idx]));
+ } else try sema.arena.dupe(u8, ip.stringToSlice(field_names[field_idx]));
// The error info needs the field types, but we can't reuse sub_peer_tys
// since the recursive call may have clobbered it.
@@ -33892,11 +33900,11 @@ fn resolvePeerTypesInner(
field_val.* = if (comptime_val) |v| v.toIntern() else .none;
}
- const final_ty = try mod.intern(.{ .anon_struct_type = .{
+ const final_ty = try ip.getAnonStructType(mod.gpa, .{
.types = field_types,
.names = if (is_tuple) &.{} else field_names,
.values = field_vals,
- } });
+ });
return .{ .success = final_ty.toType() };
},
@@ -34491,6 +34499,7 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
/// be resolved.
pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void {
const mod = sema.mod;
+ const ip = &mod.intern_pool;
switch (ty.zigTypeTag(mod)) {
.Pointer => {
return sema.resolveTypeFully(ty.childType(mod));
@@ -34498,7 +34507,7 @@ pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void {
.Struct => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
.struct_type => return sema.resolveStructFully(ty),
.anon_struct_type => |tuple| {
- for (tuple.types) |field_ty| {
+ for (tuple.types.get(ip)) |field_ty| {
try sema.resolveTypeFully(field_ty.toType());
}
},
@@ -34518,7 +34527,6 @@ pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void {
// the function is instantiated.
return;
}
- const ip = &mod.intern_pool;
for (0..info.param_types.len) |i| {
const param_ty = info.param_types.get(ip)[i];
try sema.resolveTypeFully(param_ty.toType());
@@ -36133,7 +36141,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
},
.anon_struct_type => |tuple| {
- for (tuple.values) |val| {
+ for (tuple.values.get(ip)) |val| {
if (val == .none) return null;
}
// In this case the struct has all comptime-known fields and
@@ -36141,7 +36149,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
// TODO: write something like getCoercedInts to avoid needing to dupe
return (try mod.intern(.{ .aggregate = .{
.ty = ty.toIntern(),
- .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values) },
+ .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values.get(ip)) },
} })).toValue();
},
@@ -36611,7 +36619,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
}
},
.anon_struct_type => |tuple| {
- for (tuple.types, tuple.values) |field_ty, val| {
+ for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
const have_comptime_val = val != .none;
if (!have_comptime_val and try sema.typeRequiresComptime(field_ty.toType())) {
return true;
@@ -36784,8 +36792,9 @@ fn anonStructFieldIndex(
field_src: LazySrcLoc,
) !u32 {
const mod = sema.mod;
- switch (mod.intern_pool.indexToKey(struct_ty.toIntern())) {
- .anon_struct_type => |anon_struct_type| for (anon_struct_type.names, 0..) |name, i| {
+ const ip = &mod.intern_pool;
+ switch (ip.indexToKey(struct_ty.toIntern())) {
+ .anon_struct_type => |anon_struct_type| for (anon_struct_type.names.get(ip), 0..) |name, i| {
if (name == field_name) return @intCast(i);
},
.struct_type => |struct_type| if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| {
@@ -36798,7 +36807,7 @@ fn anonStructFieldIndex(
else => unreachable,
}
return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{
- field_name.fmt(&mod.intern_pool), struct_ty.fmt(sema.mod),
+ field_name.fmt(ip), struct_ty.fmt(sema.mod),
});
}