| author | |
| committer | |
| log | 6a62a15ecde10300f6281e2d49fed34031d5f68a |
| tree | d0c5e7abd7ef2a1bc2b1d4ee283354f1da25322b |
| parent | a7661f115dccf26b141557c923171f325cdc2757 |
| parent | c7e45aebafef0372fe231816eeffd18198240f14 |
| signature |
Sema: fix UAF in zirClosureGet 10 files changed, 176 insertions(+), 32 deletions(-)
src/Module.zig+10| ... | ... | @@ -345,6 +345,15 @@ pub const CaptureScope = struct { |
| 345 | 345 | /// During sema, this map is backed by the gpa. Once sema completes, |
| 346 | 346 | /// it is reallocated using the value_arena. |
| 347 | 347 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, |
| 348 | ||
| 349 | pub fn failed(noalias self: *const @This()) bool { | |
| 350 | return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32); | |
| 351 | } | |
| 352 | ||
| 353 | pub fn fail(noalias self: *@This()) void { | |
| 354 | self.captures.available = 0; | |
| 355 | self.captures.size = std.math.maxInt(u32); | |
| 356 | } | |
| 348 | 357 | }; |
| 349 | 358 | |
| 350 | 359 | pub const WipCaptureScope = struct { |
| ... | ... | @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { |
| 383 | 392 | pub fn deinit(noalias self: *@This()) void { |
| 384 | 393 | if (!self.finalized) { |
| 385 | 394 | self.scope.captures.deinit(self.gpa); |
| 395 | self.scope.fail(); | |
| 386 | 396 | } |
| 387 | 397 | self.* = undefined; |
| 388 | 398 | } |
src/Sema.zig+32-30| ... | ... | @@ -5956,7 +5956,6 @@ fn analyzeCall( |
| 5956 | 5956 | error.NeededSourceLocation => { |
| 5957 | 5957 | _ = sema.inst_map.remove(inst); |
| 5958 | 5958 | const decl = sema.mod.declPtr(block.src_decl); |
| 5959 | child_block.src_decl = block.src_decl; | |
| 5960 | 5959 | try sema.analyzeInlineCallArg( |
| 5961 | 5960 | block, |
| 5962 | 5961 | &child_block, |
| ... | ... | @@ -13740,6 +13739,16 @@ fn zirClosureGet( |
| 13740 | 13739 | const tv = while (true) { |
| 13741 | 13740 | // Note: We don't need to add a dependency here, because |
| 13742 | 13741 | // decls always depend on their lexical parents. |
| 13742 | ||
| 13743 | // Fail this decl if a scope it depended on failed. | |
| 13744 | if (scope.failed()) { | |
| 13745 | if (sema.owner_func) |owner_func| { | |
| 13746 | owner_func.state = .dependency_failure; | |
| 13747 | } else { | |
| 13748 | sema.owner_decl.analysis = .dependency_failure; | |
| 13749 | } | |
| 13750 | return error.AnalysisFail; | |
| 13751 | } | |
| 13743 | 13752 | if (scope.captures.getPtr(inst_data.inst)) |tv| { |
| 13744 | 13753 | break tv; |
| 13745 | 13754 | } |
| ... | ... | @@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 |
| 18076 | 18085 | const target = sema.mod.getTarget(); |
| 18077 | 18086 | |
| 18078 | 18087 | try sema.resolveTypeLayout(block, lhs_src, ty); |
| 18079 | switch (ty.tag()) { | |
| 18080 | .@"struct", .tuple, .anon_struct => {}, | |
| 18088 | switch (ty.zigTypeTag()) { | |
| 18089 | .Struct => {}, | |
| 18081 | 18090 | else => { |
| 18082 | 18091 | const msg = msg: { |
| 18083 | 18092 | 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 |
| 19617 | 19626 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19618 | 19627 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19619 | 19628 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19620 | const dest_ptr = try sema.resolveInst(extra.dest); | |
| 19621 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 19629 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); | |
| 19622 | 19630 | |
| 19623 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | |
| 19624 | if (dest_ptr_ty.isConstPtr()) { | |
| 19625 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | |
| 19626 | } | |
| 19631 | // TODO AstGen's coerced_ty cannot handle volatile here | |
| 19632 | var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; | |
| 19633 | dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); | |
| 19634 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info); | |
| 19635 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); | |
| 19627 | 19636 | |
| 19628 | 19637 | const uncasted_src_ptr = try sema.resolveInst(extra.source); |
| 19629 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); | |
| 19630 | try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); | |
| 19631 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; | |
| 19632 | const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ | |
| 19633 | .pointee_type = dest_ptr_ty.elemType2(), | |
| 19634 | .@"align" = src_ptr_info.@"align", | |
| 19635 | .@"addrspace" = src_ptr_info.@"addrspace", | |
| 19636 | .mutable = false, | |
| 19637 | .@"allowzero" = src_ptr_info.@"allowzero", | |
| 19638 | .@"volatile" = src_ptr_info.@"volatile", | |
| 19639 | .size = .Many, | |
| 19640 | }); | |
| 19641 | const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); | |
| 19638 | var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data; | |
| 19639 | src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr(); | |
| 19640 | const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info); | |
| 19641 | const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src); | |
| 19642 | 19642 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19643 | 19643 | |
| 19644 | 19644 | 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 |
| 19674 | 19674 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19675 | 19675 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19676 | 19676 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19677 | const dest_ptr = try sema.resolveInst(extra.dest); | |
| 19678 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 19679 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | |
| 19680 | if (dest_ptr_ty.isConstPtr()) { | |
| 19681 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | |
| 19682 | } | |
| 19683 | const elem_ty = dest_ptr_ty.elemType2(); | |
| 19684 | const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src); | |
| 19677 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); | |
| 19678 | ||
| 19679 | // TODO AstGen's coerced_ty cannot handle volatile here | |
| 19680 | var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; | |
| 19681 | ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); | |
| 19682 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); | |
| 19683 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); | |
| 19684 | ||
| 19685 | const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src); | |
| 19685 | 19686 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19686 | 19687 | |
| 19687 | 19688 | 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 |
| 26013 | 26014 | .pointee_type = decl_tv.ty, |
| 26014 | 26015 | .mutable = false, |
| 26015 | 26016 | .@"addrspace" = decl.@"addrspace", |
| 26017 | .@"align" = decl.@"align", | |
| 26016 | 26018 | }), |
| 26017 | 26019 | try Value.Tag.decl_ref.create(sema.arena, decl_index), |
| 26018 | 26020 | ); |
src/codegen/llvm.zig+10-1| ... | ... | @@ -9204,6 +9204,12 @@ pub const FuncGen = struct { |
| 9204 | 9204 | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); |
| 9205 | 9205 | } |
| 9206 | 9206 | |
| 9207 | if (info.pointee_type.isPtrAtRuntime()) { | |
| 9208 | const same_size_int = self.context.intType(elem_bits); | |
| 9209 | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); | |
| 9210 | return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); | |
| 9211 | } | |
| 9212 | ||
| 9207 | 9213 | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); |
| 9208 | 9214 | } |
| 9209 | 9215 | |
| ... | ... | @@ -9235,7 +9241,10 @@ pub const FuncGen = struct { |
| 9235 | 9241 | // Convert to equally-sized integer type in order to perform the bit |
| 9236 | 9242 | // operations on the value to store |
| 9237 | 9243 | const value_bits_type = self.context.intType(elem_bits); |
| 9238 | const value_bits = self.builder.buildBitCast(elem, value_bits_type, ""); | |
| 9244 | const value_bits = if (elem_ty.isPtrAtRuntime()) | |
| 9245 | self.builder.buildPtrToInt(elem, value_bits_type, "") | |
| 9246 | else | |
| 9247 | self.builder.buildBitCast(elem, value_bits_type, ""); | |
| 9239 | 9248 | |
| 9240 | 9249 | var mask_val = value_bits_type.constAllOnes(); |
| 9241 | 9250 | mask_val = mask_val.constZExt(containing_int_ty); |
src/translate_c.zig+1-1| ... | ... | @@ -1167,7 +1167,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1167 | 1167 | } |
| 1168 | 1168 | |
| 1169 | 1169 | if (!c.zig_is_stage1 and is_packed) { |
| 1170 | return failDecl(c, record_loc, bare_name, "cannot translate packed record union", .{}); | |
| 1170 | return failDecl(c, record_loc, name, "cannot translate packed record union", .{}); | |
| 1171 | 1171 | } |
| 1172 | 1172 | |
| 1173 | 1173 | const record_payload = try c.arena.create(ast.Payload.Record); |
test/behavior.zig+1| ... | ... | @@ -86,6 +86,7 @@ test { |
| 86 | 86 | _ = @import("behavior/bugs/12430.zig"); |
| 87 | 87 | _ = @import("behavior/bugs/12486.zig"); |
| 88 | 88 | _ = @import("behavior/bugs/12680.zig"); |
| 89 | _ = @import("behavior/bugs/12776.zig"); | |
| 89 | 90 | _ = @import("behavior/byteswap.zig"); |
| 90 | 91 | _ = @import("behavior/byval_arg_var.zig"); |
| 91 | 92 | _ = @import("behavior/call.zig"); |
test/behavior/bugs/12776.zig created+42| ... | ... | @@ -0,0 +1,42 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | const RAM = struct { | |
| 5 | data: [0xFFFF + 1]u8, | |
| 6 | fn new() !RAM { | |
| 7 | return RAM{ .data = [_]u8{0} ** 0x10000 }; | |
| 8 | } | |
| 9 | fn get(self: *RAM, addr: u16) u8 { | |
| 10 | return self.data[addr]; | |
| 11 | } | |
| 12 | }; | |
| 13 | ||
| 14 | const CPU = packed struct { | |
| 15 | interrupts: bool, | |
| 16 | ram: *RAM, | |
| 17 | fn new(ram: *RAM) !CPU { | |
| 18 | return CPU{ | |
| 19 | .ram = ram, | |
| 20 | .interrupts = false, | |
| 21 | }; | |
| 22 | } | |
| 23 | fn tick(self: *CPU) !void { | |
| 24 | var queued_interrupts = self.ram.get(0xFFFF) & self.ram.get(0xFF0F); | |
| 25 | if (self.interrupts and queued_interrupts != 0) { | |
| 26 | self.interrupts = false; | |
| 27 | } | |
| 28 | } | |
| 29 | }; | |
| 30 | ||
| 31 | test { | |
| 32 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 33 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 34 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 35 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 36 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 37 | ||
| 38 | var ram = try RAM.new(); | |
| 39 | var cpu = try CPU.new(&ram); | |
| 40 | try cpu.tick(); | |
| 41 | try std.testing.expect(cpu.interrupts == false); | |
| 42 | } |
test/behavior/pointers.zig+11| ... | ... | @@ -486,3 +486,14 @@ test "array slicing to slice" { |
| 486 | 486 | try S.doTheTest(); |
| 487 | 487 | comptime try S.doTheTest(); |
| 488 | 488 | } |
| 489 | ||
| 490 | test "pointer to constant decl preserves alignment" { | |
| 491 | const S = struct { | |
| 492 | a: u8, | |
| 493 | b: u8, | |
| 494 | const aligned align(8) = @This(){ .a = 3, .b = 4 }; | |
| 495 | }; | |
| 496 | ||
| 497 | const alignment = @typeInfo(@TypeOf(&S.aligned)).Pointer.alignment; | |
| 498 | try std.testing.expect(alignment == 8); | |
| 499 | } |
test/cases/compile_errors/closure_get_depends_on_failed_decl.zig created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | pub inline fn instanceRequestAdapter() void {} | |
| 2 | ||
| 3 | pub inline fn requestAdapter( | |
| 4 | comptime callbackArg: fn () callconv(.Inline) void, | |
| 5 | ) void { | |
| 6 | _ = (struct { | |
| 7 | pub fn callback() callconv(.C) void { | |
| 8 | callbackArg(); | |
| 9 | } | |
| 10 | }).callback; | |
| 11 | instanceRequestAdapter(undefined); // note wrong number of arguments here | |
| 12 | } | |
| 13 | ||
| 14 | inline fn foo() void {} | |
| 15 | ||
| 16 | pub export fn entry() void { | |
| 17 | requestAdapter(foo); | |
| 18 | } | |
| 19 | ||
| 20 | // error | |
| 21 | // backend=stage2 | |
| 22 | // target=native | |
| 23 | // | |
| 24 | // :11:5: error: expected 0 argument(s), found 1 | |
| 25 | // :1:12: note: function declared here | |
| 26 | // :17:19: note: called from here |
test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | fn Observable(comptime T: type) type { | |
| 2 | return struct { | |
| 3 | fn map(Src: T, Dst: anytype, function: fn (T) Dst) Dst { | |
| 4 | _ = Src; | |
| 5 | _ = function; | |
| 6 | return Observable(Dst); | |
| 7 | } | |
| 8 | }; | |
| 9 | } | |
| 10 | ||
| 11 | fn u32Tou64(x: u32) u64 { | |
| 12 | _ = x; | |
| 13 | return 0; | |
| 14 | } | |
| 15 | ||
| 16 | pub export fn entry() void { | |
| 17 | Observable(u32).map(u32, u64, u32Tou64(0)); | |
| 18 | } | |
| 19 | ||
| 20 | // error | |
| 21 | // backend=stage2 | |
| 22 | // target=native | |
| 23 | // | |
| 24 | // :17:25: error: expected type 'u32', found 'type' |
test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | pub export fn entry() void { | |
| 2 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | |
| 3 | var slice: []u8 = &buf; | |
| 4 | const a: u32 = 1234; | |
| 5 | @memcpy(slice, @ptrCast([*]const u8, &a), 4); | |
| 6 | } | |
| 7 | pub export fn entry1() void { | |
| 8 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | |
| 9 | var ptr: *u8 = &buf[0]; | |
| 10 | @memcpy(ptr, 0, 4); | |
| 11 | } | |
| 12 | ||
| 13 | // error | |
| 14 | // backend=stage2 | |
| 15 | // target=native | |
| 16 | // | |
| 17 | // :5:13: error: expected type '[*]u8', found '[]u8' | |
| 18 | // :10:13: error: expected type '[*]u8', found '*u8' | |
| 19 | // :10:13: note: a single pointer cannot cast into a many pointer |