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...@@ -4554,9 +4554,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
4554 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {4554 if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
4555 return Air.Inst.Ref.anyerror_type;4555 return Air.Inst.Ref.anyerror_type;
4556 }4556 }
4557 // When we support inferred error sets, we'll want to use a data structure that can4557 // Resolve both error sets now.
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.
4560 var set: std.StringHashMapUnmanaged(void) = .{};4558 var set: std.StringHashMapUnmanaged(void) = .{};
4561 defer set.deinit(sema.gpa);4559 defer set.deinit(sema.gpa);
45624560
...@@ -4565,6 +4563,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -4565,6 +4563,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
4565 const name = lhs_ty.castTag(.error_set_single).?.data;4563 const name = lhs_ty.castTag(.error_set_single).?.data;
4566 try set.put(sema.gpa, name, {});4564 try set.put(sema.gpa, name, {});
4567 },4565 },
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 },
4568 .error_set => {4572 .error_set => {
4569 const lhs_set = lhs_ty.castTag(.error_set).?.data;4573 const lhs_set = lhs_ty.castTag(.error_set).?.data;
4570 try set.ensureUnusedCapacity(sema.gpa, lhs_set.names_len);4574 try set.ensureUnusedCapacity(sema.gpa, lhs_set.names_len);
...@@ -4579,6 +4583,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -4579,6 +4583,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
4579 const name = rhs_ty.castTag(.error_set_single).?.data;4583 const name = rhs_ty.castTag(.error_set_single).?.data;
4580 try set.put(sema.gpa, name, {});4584 try set.put(sema.gpa, name, {});
4581 },4585 },
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 },
4582 .error_set => {4592 .error_set => {
4583 const rhs_set = rhs_ty.castTag(.error_set).?.data;4593 const rhs_set = rhs_ty.castTag(.error_set).?.data;
4584 try set.ensureUnusedCapacity(sema.gpa, rhs_set.names_len);4594 try set.ensureUnusedCapacity(sema.gpa, rhs_set.names_len);
...@@ -4589,22 +4599,25 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -4589,22 +4599,25 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
4589 else => unreachable,4599 else => unreachable,
4590 }4600 }
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());
4593 var it = set.keyIterator();4608 var it = set.keyIterator();
4594 var i: usize = 0;4609 var i: usize = 0;
4595 while (it.next()) |key| : (i += 1) {4610 while (it.next()) |key| : (i += 1) {
4596 new_names[i] = key.*;4611 new_names[i] = key.*;
4597 }4612 }
45984613
4599 const new_error_set = try sema.arena.create(Module.ErrorSet);4614 const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), new_names);
4600 new_error_set.* = .{4615 const err_set_decl = try anon_decl.finish(
4601 .owner_decl = sema.owner_decl,4616 Type.type,
4602 .node_offset = inst_data.src_node,4617 try Value.Tag.ty.create(anon_decl.arena(), err_set_ty),
4603 .names_ptr = new_names.ptr,4618 );
4604 .names_len = @intCast(u32, new_names.len),4619 try sema.mod.declareDeclDependency(sema.owner_decl, err_set_decl);
4605 };4620 return sema.addType(err_set_ty);
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));
4608}4621}
46094622
4610fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {4623fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -14788,6 +14801,7 @@ fn typeHasOnePossibleValue(...@@ -14788,6 +14801,7 @@ fn typeHasOnePossibleValue(
14788 .error_set,14801 .error_set,
14789 .error_set_single,14802 .error_set_single,
14790 .error_set_inferred,14803 .error_set_inferred,
14804 .error_set_merged,
14791 .@"opaque",14805 .@"opaque",
14792 .var_args_param,14806 .var_args_param,
14793 .manyptr_u8,14807 .manyptr_u8,
src/type.zig+50-2
...@@ -61,11 +61,17 @@ pub const Type = extern union {...@@ -61,11 +61,17 @@ pub const Type = extern union {
61 .c_longdouble,61 .c_longdouble,
62 => return .Float,62 => return .Float,
6363
64 .error_set,
65 .error_set_single,
66 .anyerror,
67 .error_set_inferred,
68 .error_set_merged,
69 => return .ErrorSet,
70
64 .c_void, .@"opaque" => return .Opaque,71 .c_void, .@"opaque" => return .Opaque,
65 .bool => return .Bool,72 .bool => return .Bool,
66 .void => return .Void,73 .void => return .Void,
67 .type => return .Type,74 .type => return .Type,
68 .error_set, .error_set_single, .anyerror, .error_set_inferred => return .ErrorSet,
69 .comptime_int => return .ComptimeInt,75 .comptime_int => return .ComptimeInt,
70 .comptime_float => return .ComptimeFloat,76 .comptime_float => return .ComptimeFloat,
71 .noreturn => return .NoReturn,77 .noreturn => return .NoReturn,
...@@ -608,6 +614,9 @@ pub const Type = extern union {...@@ -608,6 +614,9 @@ pub const Type = extern union {
608 return true;614 return true;
609 },615 },
610 .ErrorSet => {616 .ErrorSet => {
617 // TODO: revisit the language specification for how to evaluate equality
618 // for error set types.
619
611 if (a.tag() == .anyerror and b.tag() == .anyerror) {620 if (a.tag() == .anyerror and b.tag() == .anyerror) {
612 return true;621 return true;
613 }622 }
...@@ -892,6 +901,14 @@ pub const Type = extern union {...@@ -892,6 +901,14 @@ pub const Type = extern union {
892 .payload = try payload.payload.copy(allocator),901 .payload = try payload.payload.copy(allocator),
893 });902 });
894 },903 },
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 },
895 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),912 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
896 .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred),913 .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred),
897 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),914 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
...@@ -1185,6 +1202,16 @@ pub const Type = extern union {...@@ -1185,6 +1202,16 @@ pub const Type = extern union {
1185 const func = ty.castTag(.error_set_inferred).?.data.func;1202 const func = ty.castTag(.error_set_inferred).?.data.func;
1186 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});1203 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
1187 },1204 },
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 },
1188 .error_set_single => {1215 .error_set_single => {
1189 const name = ty.castTag(.error_set_single).?.data;1216 const name = ty.castTag(.error_set_single).?.data;
1190 return writer.print("error{{{s}}}", .{name});1217 return writer.print("error{{{s}}}", .{name});
...@@ -1365,6 +1392,7 @@ pub const Type = extern union {...@@ -1365,6 +1392,7 @@ pub const Type = extern union {
1365 .error_set,1392 .error_set,
1366 .error_set_single,1393 .error_set_single,
1367 .error_set_inferred,1394 .error_set_inferred,
1395 .error_set_merged,
1368 .@"opaque",1396 .@"opaque",
1369 .generic_poison,1397 .generic_poison,
1370 .array_u8,1398 .array_u8,
...@@ -1525,6 +1553,7 @@ pub const Type = extern union {...@@ -1525,6 +1553,7 @@ pub const Type = extern union {
1525 .error_set,1553 .error_set,
1526 .error_set_single,1554 .error_set_single,
1527 .error_set_inferred,1555 .error_set_inferred,
1556 .error_set_merged,
1528 .manyptr_u8,1557 .manyptr_u8,
1529 .manyptr_const_u8,1558 .manyptr_const_u8,
1530 .atomic_order,1559 .atomic_order,
...@@ -1783,6 +1812,7 @@ pub const Type = extern union {...@@ -1783,6 +1812,7 @@ pub const Type = extern union {
1783 .anyerror_void_error_union,1812 .anyerror_void_error_union,
1784 .anyerror,1813 .anyerror,
1785 .error_set_inferred,1814 .error_set_inferred,
1815 .error_set_merged,
1786 => return 2, // TODO revisit this when we have the concept of the error tag type1816 => return 2, // TODO revisit this when we have the concept of the error tag type
17871817
1788 .array, .array_sentinel => return self.elemType().abiAlignment(target),1818 .array, .array_sentinel => return self.elemType().abiAlignment(target),
...@@ -2021,6 +2051,7 @@ pub const Type = extern union {...@@ -2021,6 +2051,7 @@ pub const Type = extern union {
2021 .anyerror_void_error_union,2051 .anyerror_void_error_union,
2022 .anyerror,2052 .anyerror,
2023 .error_set_inferred,2053 .error_set_inferred,
2054 .error_set_merged,
2024 => return 2, // TODO revisit this when we have the concept of the error tag type2055 => return 2, // TODO revisit this when we have the concept of the error tag type
20252056
2026 .int_signed, .int_unsigned => {2057 .int_signed, .int_unsigned => {
...@@ -2199,6 +2230,7 @@ pub const Type = extern union {...@@ -2199,6 +2230,7 @@ pub const Type = extern union {
2199 .anyerror_void_error_union,2230 .anyerror_void_error_union,
2200 .anyerror,2231 .anyerror,
2201 .error_set_inferred,2232 .error_set_inferred,
2233 .error_set_merged,
2202 => return 16, // TODO revisit this when we have the concept of the error tag type2234 => return 16, // TODO revisit this when we have the concept of the error tag type
22032235
2204 .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data,2236 .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data,
...@@ -2961,7 +2993,7 @@ pub const Type = extern union {...@@ -2961,7 +2993,7 @@ pub const Type = extern union {
2961 return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) };2993 return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) };
2962 },2994 },
29632995
2964 .error_set, .error_set_single, .anyerror, .error_set_inferred => {2996 .error_set, .error_set_single, .anyerror, .error_set_inferred, .error_set_merged => {
2965 // TODO revisit this when error sets support custom int types2997 // TODO revisit this when error sets support custom int types
2966 return .{ .signedness = .unsigned, .bits = 16 };2998 return .{ .signedness = .unsigned, .bits = 16 };
2967 },2999 },
...@@ -3250,6 +3282,7 @@ pub const Type = extern union {...@@ -3250,6 +3282,7 @@ pub const Type = extern union {
3250 .error_set,3282 .error_set,
3251 .error_set_single,3283 .error_set_single,
3252 .error_set_inferred,3284 .error_set_inferred,
3285 .error_set_merged,
3253 .@"opaque",3286 .@"opaque",
3254 .var_args_param,3287 .var_args_param,
3255 .manyptr_u8,3288 .manyptr_u8,
...@@ -3882,6 +3915,7 @@ pub const Type = extern union {...@@ -3882,6 +3915,7 @@ pub const Type = extern union {
3882 error_set_single,3915 error_set_single,
3883 /// The type is the inferred error set of a specific function.3916 /// The type is the inferred error set of a specific function.
3884 error_set_inferred,3917 error_set_inferred,
3918 error_set_merged,
3885 empty_struct,3919 empty_struct,
3886 @"opaque",3920 @"opaque",
3887 @"struct",3921 @"struct",
...@@ -3986,6 +4020,7 @@ pub const Type = extern union {...@@ -3986,6 +4020,7 @@ pub const Type = extern union {
39864020
3987 .error_set => Payload.ErrorSet,4021 .error_set => Payload.ErrorSet,
3988 .error_set_inferred => Payload.ErrorSetInferred,4022 .error_set_inferred => Payload.ErrorSetInferred,
4023 .error_set_merged => Payload.ErrorSetMerged,
39894024
3990 .array, .vector => Payload.Array,4025 .array, .vector => Payload.Array,
3991 .array_sentinel => Payload.ArraySentinel,4026 .array_sentinel => Payload.ArraySentinel,
...@@ -4090,6 +4125,13 @@ pub const Type = extern union {...@@ -4090,6 +4125,13 @@ pub const Type = extern union {
4090 data: *Module.ErrorSet,4125 data: *Module.ErrorSet,
4091 };4126 };
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
4093 pub const ErrorSetInferred = struct {4135 pub const ErrorSetInferred = struct {
4094 pub const base_tag = Tag.error_set_inferred;4136 pub const base_tag = Tag.error_set_inferred;
40954137
...@@ -4125,6 +4167,12 @@ pub const Type = extern union {...@@ -4125,6 +4167,12 @@ pub const Type = extern union {
4125 try self.map.put(gpa, entry.key_ptr.*, {});4167 try self.map.put(gpa, entry.key_ptr.*, {});
4126 }4168 }
4127 },4169 },
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 },
4128 .anyerror => {4176 .anyerror => {
4129 self.is_anyerror = true;4177 self.is_anyerror = true;
4130 },4178 },