authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-30 23:56:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-30 23:56:57-07:00
log8f9d857932dfb3abb97852dacc90c88a613a1b25
treeb61ca956811af80bdc8d9bc0115ca9d377ad6c76
parent7355a201336c8e3892427e5932fe5cdd46cf96df

Sema: fix error set merging creating references to invalid memory

Trying to use std.heap.page_allocator with stage2 now results in (incorrect) compile errors rather than UAF.

2 files changed, 77 insertions(+), 15 deletions(-)

src/Sema.zig+27-13
......@@ -4554,9 +4554,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
45544554 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
45554555 return Air.Inst.Ref.anyerror_type;
45564556 }
4557 // When we support inferred error sets, we'll want to use a data structure that can
4558 // represent a merged set of errors without forcing them to be resolved here. Until then
4559 // we re-use the same data structure that is used for explicit error set declarations.
4557 // Resolve both error sets now.
45604558 var set: std.StringHashMapUnmanaged(void) = .{};
45614559 defer set.deinit(sema.gpa);
45624560
......@@ -4565,6 +4563,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
45654563 const name = lhs_ty.castTag(.error_set_single).?.data;
45664564 try set.put(sema.gpa, name, {});
45674565 },
4566 .error_set_merged => {
4567 const names = lhs_ty.castTag(.error_set_merged).?.data;
4568 for (names) |name| {
4569 try set.put(sema.gpa, name, {});
4570 }
4571 },
45684572 .error_set => {
45694573 const lhs_set = lhs_ty.castTag(.error_set).?.data;
45704574 try set.ensureUnusedCapacity(sema.gpa, lhs_set.names_len);
......@@ -4579,6 +4583,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
45794583 const name = rhs_ty.castTag(.error_set_single).?.data;
45804584 try set.put(sema.gpa, name, {});
45814585 },
4586 .error_set_merged => {
4587 const names = rhs_ty.castTag(.error_set_merged).?.data;
4588 for (names) |name| {
4589 try set.put(sema.gpa, name, {});
4590 }
4591 },
45824592 .error_set => {
45834593 const rhs_set = rhs_ty.castTag(.error_set).?.data;
45844594 try set.ensureUnusedCapacity(sema.gpa, rhs_set.names_len);
......@@ -4589,22 +4599,25 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
45894599 else => unreachable,
45904600 }
45914601
4592 const new_names = try sema.arena.alloc([]const u8, set.count());
4602 // TODO do we really want to create a Decl for this?
4603 // The reason we do it right now is for memory management.
4604 var anon_decl = try block.startAnonDecl();
4605 defer anon_decl.deinit();
4606
4607 const new_names = try anon_decl.arena().alloc([]const u8, set.count());
45934608 var it = set.keyIterator();
45944609 var i: usize = 0;
45954610 while (it.next()) |key| : (i += 1) {
45964611 new_names[i] = key.*;
45974612 }
45984613
4599 const new_error_set = try sema.arena.create(Module.ErrorSet);
4600 new_error_set.* = .{
4601 .owner_decl = sema.owner_decl,
4602 .node_offset = inst_data.src_node,
4603 .names_ptr = new_names.ptr,
4604 .names_len = @intCast(u32, new_names.len),
4605 };
4606 const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set);
4607 return sema.addConstant(Type.type, try Value.Tag.ty.create(sema.arena, error_set_ty));
4614 const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), new_names);
4615 const err_set_decl = try anon_decl.finish(
4616 Type.type,
4617 try Value.Tag.ty.create(anon_decl.arena(), err_set_ty),
4618 );
4619 try sema.mod.declareDeclDependency(sema.owner_decl, err_set_decl);
4620 return sema.addType(err_set_ty);
46084621}
46094622
46104623fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -14788,6 +14801,7 @@ fn typeHasOnePossibleValue(
1478814801 .error_set,
1478914802 .error_set_single,
1479014803 .error_set_inferred,
14804 .error_set_merged,
1479114805 .@"opaque",
1479214806 .var_args_param,
1479314807 .manyptr_u8,
src/type.zig+50-2
......@@ -61,11 +61,17 @@ pub const Type = extern union {
6161 .c_longdouble,
6262 => return .Float,
6363
64 .error_set,
65 .error_set_single,
66 .anyerror,
67 .error_set_inferred,
68 .error_set_merged,
69 => return .ErrorSet,
70
6471 .c_void, .@"opaque" => return .Opaque,
6572 .bool => return .Bool,
6673 .void => return .Void,
6774 .type => return .Type,
68 .error_set, .error_set_single, .anyerror, .error_set_inferred => return .ErrorSet,
6975 .comptime_int => return .ComptimeInt,
7076 .comptime_float => return .ComptimeFloat,
7177 .noreturn => return .NoReturn,
......@@ -608,6 +614,9 @@ pub const Type = extern union {
608614 return true;
609615 },
610616 .ErrorSet => {
617 // TODO: revisit the language specification for how to evaluate equality
618 // for error set types.
619
611620 if (a.tag() == .anyerror and b.tag() == .anyerror) {
612621 return true;
613622 }
......@@ -892,6 +901,14 @@ pub const Type = extern union {
892901 .payload = try payload.payload.copy(allocator),
893902 });
894903 },
904 .error_set_merged => {
905 const names = self.castTag(.error_set_merged).?.data;
906 const duped_names = try allocator.alloc([]const u8, names.len);
907 for (duped_names) |*name, i| {
908 name.* = try allocator.dupe(u8, names[i]);
909 }
910 return Tag.error_set_merged.create(allocator, duped_names);
911 },
895912 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
896913 .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred),
897914 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
......@@ -1185,6 +1202,16 @@ pub const Type = extern union {
11851202 const func = ty.castTag(.error_set_inferred).?.data.func;
11861203 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
11871204 },
1205 .error_set_merged => {
1206 const names = ty.castTag(.error_set_merged).?.data;
1207 try writer.writeAll("error{");
1208 for (names) |name, i| {
1209 if (i != 0) try writer.writeByte(',');
1210 try writer.writeAll(name);
1211 }
1212 try writer.writeAll("}");
1213 return;
1214 },
11881215 .error_set_single => {
11891216 const name = ty.castTag(.error_set_single).?.data;
11901217 return writer.print("error{{{s}}}", .{name});
......@@ -1365,6 +1392,7 @@ pub const Type = extern union {
13651392 .error_set,
13661393 .error_set_single,
13671394 .error_set_inferred,
1395 .error_set_merged,
13681396 .@"opaque",
13691397 .generic_poison,
13701398 .array_u8,
......@@ -1525,6 +1553,7 @@ pub const Type = extern union {
15251553 .error_set,
15261554 .error_set_single,
15271555 .error_set_inferred,
1556 .error_set_merged,
15281557 .manyptr_u8,
15291558 .manyptr_const_u8,
15301559 .atomic_order,
......@@ -1783,6 +1812,7 @@ pub const Type = extern union {
17831812 .anyerror_void_error_union,
17841813 .anyerror,
17851814 .error_set_inferred,
1815 .error_set_merged,
17861816 => return 2, // TODO revisit this when we have the concept of the error tag type
17871817
17881818 .array, .array_sentinel => return self.elemType().abiAlignment(target),
......@@ -2021,6 +2051,7 @@ pub const Type = extern union {
20212051 .anyerror_void_error_union,
20222052 .anyerror,
20232053 .error_set_inferred,
2054 .error_set_merged,
20242055 => return 2, // TODO revisit this when we have the concept of the error tag type
20252056
20262057 .int_signed, .int_unsigned => {
......@@ -2199,6 +2230,7 @@ pub const Type = extern union {
21992230 .anyerror_void_error_union,
22002231 .anyerror,
22012232 .error_set_inferred,
2233 .error_set_merged,
22022234 => return 16, // TODO revisit this when we have the concept of the error tag type
22032235
22042236 .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data,
......@@ -2961,7 +2993,7 @@ pub const Type = extern union {
29612993 return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) };
29622994 },
29632995
2964 .error_set, .error_set_single, .anyerror, .error_set_inferred => {
2996 .error_set, .error_set_single, .anyerror, .error_set_inferred, .error_set_merged => {
29652997 // TODO revisit this when error sets support custom int types
29662998 return .{ .signedness = .unsigned, .bits = 16 };
29672999 },
......@@ -3250,6 +3282,7 @@ pub const Type = extern union {
32503282 .error_set,
32513283 .error_set_single,
32523284 .error_set_inferred,
3285 .error_set_merged,
32533286 .@"opaque",
32543287 .var_args_param,
32553288 .manyptr_u8,
......@@ -3882,6 +3915,7 @@ pub const Type = extern union {
38823915 error_set_single,
38833916 /// The type is the inferred error set of a specific function.
38843917 error_set_inferred,
3918 error_set_merged,
38853919 empty_struct,
38863920 @"opaque",
38873921 @"struct",
......@@ -3986,6 +4020,7 @@ pub const Type = extern union {
39864020
39874021 .error_set => Payload.ErrorSet,
39884022 .error_set_inferred => Payload.ErrorSetInferred,
4023 .error_set_merged => Payload.ErrorSetMerged,
39894024
39904025 .array, .vector => Payload.Array,
39914026 .array_sentinel => Payload.ArraySentinel,
......@@ -4090,6 +4125,13 @@ pub const Type = extern union {
40904125 data: *Module.ErrorSet,
40914126 };
40924127
4128 pub const ErrorSetMerged = struct {
4129 pub const base_tag = Tag.error_set_merged;
4130
4131 base: Payload = Payload{ .tag = base_tag },
4132 data: []const []const u8,
4133 };
4134
40934135 pub const ErrorSetInferred = struct {
40944136 pub const base_tag = Tag.error_set_inferred;
40954137
......@@ -4125,6 +4167,12 @@ pub const Type = extern union {
41254167 try self.map.put(gpa, entry.key_ptr.*, {});
41264168 }
41274169 },
4170 .error_set_merged => {
4171 const names = err_set_ty.castTag(.error_set_merged).?.data;
4172 for (names) |name| {
4173 try self.map.put(gpa, name, {});
4174 }
4175 },
41284176 .anyerror => {
41294177 self.is_anyerror = true;
41304178 },