aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2022-09-08 16:43:54 +0300
committerGitHub <noreply@github.com>2022-09-08 16:43:54 +0300
commit6a62a15ecde10300f6281e2d49fed34031d5f68a (patch)
treed0c5e7abd7ef2a1bc2b1d4ee283354f1da25322b
parenta7661f115dccf26b141557c923171f325cdc2757 (diff)
parentc7e45aebafef0372fe231816eeffd18198240f14 (diff)
downloadzig-6a62a15ecde10300f6281e2d49fed34031d5f68a.tar.gz
zig-6a62a15ecde10300f6281e2d49fed34031d5f68a.zip
Merge pull request #12773 from Vexu/stage2-fixes
Sema: fix UAF in zirClosureGet
-rw-r--r--src/Module.zig10
-rw-r--r--src/Sema.zig62
-rw-r--r--src/codegen/llvm.zig11
-rw-r--r--src/translate_c.zig2
-rw-r--r--test/behavior.zig1
-rw-r--r--test/behavior/bugs/12776.zig42
-rw-r--r--test/behavior/pointers.zig11
-rw-r--r--test/cases/compile_errors/closure_get_depends_on_failed_decl.zig26
-rw-r--r--test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig24
-rw-r--r--test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig19
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