diff options
| author | Veikka Tuominen <git@vexu.eu> | 2022-09-08 16:43:54 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-08 16:43:54 +0300 |
| commit | 6a62a15ecde10300f6281e2d49fed34031d5f68a (patch) | |
| tree | d0c5e7abd7ef2a1bc2b1d4ee283354f1da25322b | |
| parent | a7661f115dccf26b141557c923171f325cdc2757 (diff) | |
| parent | c7e45aebafef0372fe231816eeffd18198240f14 (diff) | |
| download | zig-6a62a15ecde10300f6281e2d49fed34031d5f68a.tar.gz zig-6a62a15ecde10300f6281e2d49fed34031d5f68a.zip | |
Merge pull request #12773 from Vexu/stage2-fixes
Sema: fix UAF in zirClosureGet
| -rw-r--r-- | src/Module.zig | 10 | ||||
| -rw-r--r-- | src/Sema.zig | 62 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 11 | ||||
| -rw-r--r-- | src/translate_c.zig | 2 | ||||
| -rw-r--r-- | test/behavior.zig | 1 | ||||
| -rw-r--r-- | test/behavior/bugs/12776.zig | 42 | ||||
| -rw-r--r-- | test/behavior/pointers.zig | 11 | ||||
| -rw-r--r-- | test/cases/compile_errors/closure_get_depends_on_failed_decl.zig | 26 | ||||
| -rw-r--r-- | test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig | 24 | ||||
| -rw-r--r-- | test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig | 19 |
10 files changed, 176 insertions, 32 deletions
diff --git a/src/Module.zig b/src/Module.zig index 3ae6c48edd..ea89225537 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -345,6 +345,15 @@ pub const CaptureScope = struct { /// During sema, this map is backed by the gpa. Once sema completes, /// it is reallocated using the value_arena. captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, + + pub fn failed(noalias self: *const @This()) bool { + return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32); + } + + pub fn fail(noalias self: *@This()) void { + self.captures.available = 0; + self.captures.size = std.math.maxInt(u32); + } }; pub const WipCaptureScope = struct { @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { pub fn deinit(noalias self: *@This()) void { if (!self.finalized) { self.scope.captures.deinit(self.gpa); + self.scope.fail(); } self.* = undefined; } diff --git a/src/Sema.zig b/src/Sema.zig index fb1638bc2a..15e891ef87 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5956,7 +5956,6 @@ fn analyzeCall( error.NeededSourceLocation => { _ = sema.inst_map.remove(inst); const decl = sema.mod.declPtr(block.src_decl); - child_block.src_decl = block.src_decl; try sema.analyzeInlineCallArg( block, &child_block, @@ -13740,6 +13739,16 @@ fn zirClosureGet( const tv = while (true) { // Note: We don't need to add a dependency here, because // decls always depend on their lexical parents. + + // Fail this decl if a scope it depended on failed. + if (scope.failed()) { + if (sema.owner_func) |owner_func| { + owner_func.state = .dependency_failure; + } else { + sema.owner_decl.analysis = .dependency_failure; + } + return error.AnalysisFail; + } if (scope.captures.getPtr(inst_data.inst)) |tv| { break tv; } @@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 const target = sema.mod.getTarget(); try sema.resolveTypeLayout(block, lhs_src, ty); - switch (ty.tag()) { - .@"struct", .tuple, .anon_struct => {}, + switch (ty.zigTypeTag()) { + .Struct => {}, else => { const msg = msg: { const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)}); @@ -19617,28 +19626,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; - const dest_ptr = try sema.resolveInst(extra.dest); - const dest_ptr_ty = sema.typeOf(dest_ptr); + const uncasted_dest_ptr = try sema.resolveInst(extra.dest); - try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); - if (dest_ptr_ty.isConstPtr()) { - return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); - } + // TODO AstGen's coerced_ty cannot handle volatile here + var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; + dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); + const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info); + const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); const uncasted_src_ptr = try sema.resolveInst(extra.source); - const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); - try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); - const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; - const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = dest_ptr_ty.elemType2(), - .@"align" = src_ptr_info.@"align", - .@"addrspace" = src_ptr_info.@"addrspace", - .mutable = false, - .@"allowzero" = src_ptr_info.@"allowzero", - .@"volatile" = src_ptr_info.@"volatile", - .size = .Many, - }); - const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); + var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data; + src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr(); + const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info); + const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src); const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { @@ -19674,14 +19674,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; - const dest_ptr = try sema.resolveInst(extra.dest); - const dest_ptr_ty = sema.typeOf(dest_ptr); - try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); - if (dest_ptr_ty.isConstPtr()) { - return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); - } - const elem_ty = dest_ptr_ty.elemType2(); - const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src); + const uncasted_dest_ptr = try sema.resolveInst(extra.dest); + + // TODO AstGen's coerced_ty cannot handle volatile here + var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; + ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); + const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); + const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); + + const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src); const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { @@ -26013,6 +26014,7 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref .pointee_type = decl_tv.ty, .mutable = false, .@"addrspace" = decl.@"addrspace", + .@"align" = decl.@"align", }), try Value.Tag.decl_ref.create(sema.arena, decl_index), ); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 80ffd7a665..043f0bbdc7 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9204,6 +9204,12 @@ pub const FuncGen = struct { return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); } + if (info.pointee_type.isPtrAtRuntime()) { + const same_size_int = self.context.intType(elem_bits); + const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); + return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); + } + return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); } @@ -9235,7 +9241,10 @@ pub const FuncGen = struct { // Convert to equally-sized integer type in order to perform the bit // operations on the value to store const value_bits_type = self.context.intType(elem_bits); - const value_bits = self.builder.buildBitCast(elem, value_bits_type, ""); + const value_bits = if (elem_ty.isPtrAtRuntime()) + self.builder.buildPtrToInt(elem, value_bits_type, "") + else + self.builder.buildBitCast(elem, value_bits_type, ""); var mask_val = value_bits_type.constAllOnes(); mask_val = mask_val.constZExt(containing_int_ty); diff --git a/src/translate_c.zig b/src/translate_c.zig index e62f5b4568..014f6b1934 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1167,7 +1167,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD } if (!c.zig_is_stage1 and is_packed) { - return failDecl(c, record_loc, bare_name, "cannot translate packed record union", .{}); + return failDecl(c, record_loc, name, "cannot translate packed record union", .{}); } const record_payload = try c.arena.create(ast.Payload.Record); diff --git a/test/behavior.zig b/test/behavior.zig index 4b55913af5..db107bcbb1 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -86,6 +86,7 @@ test { _ = @import("behavior/bugs/12430.zig"); _ = @import("behavior/bugs/12486.zig"); _ = @import("behavior/bugs/12680.zig"); + _ = @import("behavior/bugs/12776.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); diff --git a/test/behavior/bugs/12776.zig b/test/behavior/bugs/12776.zig new file mode 100644 index 0000000000..e8fe106ac7 --- /dev/null +++ b/test/behavior/bugs/12776.zig @@ -0,0 +1,42 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const RAM = struct { + data: [0xFFFF + 1]u8, + fn new() !RAM { + return RAM{ .data = [_]u8{0} ** 0x10000 }; + } + fn get(self: *RAM, addr: u16) u8 { + return self.data[addr]; + } +}; + +const CPU = packed struct { + interrupts: bool, + ram: *RAM, + fn new(ram: *RAM) !CPU { + return CPU{ + .ram = ram, + .interrupts = false, + }; + } + fn tick(self: *CPU) !void { + var queued_interrupts = self.ram.get(0xFFFF) & self.ram.get(0xFF0F); + if (self.interrupts and queued_interrupts != 0) { + self.interrupts = false; + } + } +}; + +test { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + var ram = try RAM.new(); + var cpu = try CPU.new(&ram); + try cpu.tick(); + try std.testing.expect(cpu.interrupts == false); +} diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index adbc308742..dcdea1ff80 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -486,3 +486,14 @@ test "array slicing to slice" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "pointer to constant decl preserves alignment" { + const S = struct { + a: u8, + b: u8, + const aligned align(8) = @This(){ .a = 3, .b = 4 }; + }; + + const alignment = @typeInfo(@TypeOf(&S.aligned)).Pointer.alignment; + try std.testing.expect(alignment == 8); +} diff --git a/test/cases/compile_errors/closure_get_depends_on_failed_decl.zig b/test/cases/compile_errors/closure_get_depends_on_failed_decl.zig new file mode 100644 index 0000000000..ccdbf67713 --- /dev/null +++ b/test/cases/compile_errors/closure_get_depends_on_failed_decl.zig @@ -0,0 +1,26 @@ +pub inline fn instanceRequestAdapter() void {} + +pub inline fn requestAdapter( + comptime callbackArg: fn () callconv(.Inline) void, +) void { + _ = (struct { + pub fn callback() callconv(.C) void { + callbackArg(); + } + }).callback; + instanceRequestAdapter(undefined); // note wrong number of arguments here +} + +inline fn foo() void {} + +pub export fn entry() void { + requestAdapter(foo); +} + +// error +// backend=stage2 +// target=native +// +// :11:5: error: expected 0 argument(s), found 1 +// :1:12: note: function declared here +// :17:19: note: called from here diff --git a/test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig b/test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig new file mode 100644 index 0000000000..dc533442fb --- /dev/null +++ b/test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig @@ -0,0 +1,24 @@ +fn Observable(comptime T: type) type { + return struct { + fn map(Src: T, Dst: anytype, function: fn (T) Dst) Dst { + _ = Src; + _ = function; + return Observable(Dst); + } + }; +} + +fn u32Tou64(x: u32) u64 { + _ = x; + return 0; +} + +pub export fn entry() void { + Observable(u32).map(u32, u64, u32Tou64(0)); +} + +// error +// backend=stage2 +// target=native +// +// :17:25: error: expected type 'u32', found 'type' diff --git a/test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig b/test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig new file mode 100644 index 0000000000..d3a6b7cc4b --- /dev/null +++ b/test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig @@ -0,0 +1,19 @@ +pub export fn entry() void { + var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; + var slice: []u8 = &buf; + const a: u32 = 1234; + @memcpy(slice, @ptrCast([*]const u8, &a), 4); +} +pub export fn entry1() void { + var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; + var ptr: *u8 = &buf[0]; + @memcpy(ptr, 0, 4); +} + +// error +// backend=stage2 +// target=native +// +// :5:13: error: expected type '[*]u8', found '[]u8' +// :10:13: error: expected type '[*]u8', found '*u8' +// :10:13: note: a single pointer cannot cast into a many pointer |
