authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2024-01-29 14:33:53+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-29 17:35:07-08:00
log7d75c3d3b80c86bbd47e60f85a98e8decc52c611
treec52ddac06bfb3495f19e426020c5a693d3f8f480
parent4dfca01de4fda9a195048011b3339686dce4e936

llvm: ensure returned undef is 0xaa bytes when runtime safety is enabled

Closes #13178

15 files changed, 95 insertions(+), 5 deletions(-)

src/Air.zig+7
...@@ -516,6 +516,11 @@ pub const Inst = struct {...@@ -516,6 +516,11 @@ pub const Inst = struct {
516 /// Uses the `un_op` field.516 /// Uses the `un_op` field.
517 /// Triggers `resolveTypeLayout` on the return type.517 /// Triggers `resolveTypeLayout` on the return type.
518 ret,518 ret,
519 /// Same as `ret`, except if the operand is undefined, the
520 /// returned value is 0xaa bytes, and any other safety metadata
521 /// such as Valgrind integrations should be notified of
522 /// this value being undefined.
523 ret_safe,
519 /// This instruction communicates that the function's result value is pointed to by524 /// This instruction communicates that the function's result value is pointed to by
520 /// the operand. If the function will pass the result by-ref, the operand is a525 /// the operand. If the function will pass the result by-ref, the operand is a
521 /// `ret_ptr` instruction. Otherwise, this instruction is equivalent to a `load`526 /// `ret_ptr` instruction. Otherwise, this instruction is equivalent to a `load`
...@@ -1439,6 +1444,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1439,6 +1444,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1439 .cond_br,1444 .cond_br,
1440 .switch_br,1445 .switch_br,
1441 .ret,1446 .ret,
1447 .ret_safe,
1442 .ret_load,1448 .ret_load,
1443 .unreach,1449 .unreach,
1444 .trap,1450 .trap,
...@@ -1613,6 +1619,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -1613,6 +1619,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
1613 .dbg_var_ptr,1619 .dbg_var_ptr,
1614 .dbg_var_val,1620 .dbg_var_val,
1615 .ret,1621 .ret,
1622 .ret_safe,
1616 .ret_load,1623 .ret_load,
1617 .store,1624 .store,
1618 .store_safe,1625 .store_safe,
src/Liveness.zig+2
...@@ -435,6 +435,7 @@ pub fn categorizeOperand(...@@ -435,6 +435,7 @@ pub fn categorizeOperand(
435 },435 },
436436
437 .ret,437 .ret,
438 .ret_safe,
438 .ret_load,439 .ret_load,
439 => {440 => {
440 const o = air_datas[@intFromEnum(inst)].un_op;441 const o = air_datas[@intFromEnum(inst)].un_op;
...@@ -1070,6 +1071,7 @@ fn analyzeInst(...@@ -1070,6 +1071,7 @@ fn analyzeInst(
1070 },1071 },
10711072
1072 .ret,1073 .ret,
1074 .ret_safe,
1073 .ret_load,1075 .ret_load,
1074 => {1076 => {
1075 const operand = inst_datas[@intFromEnum(inst)].un_op;1077 const operand = inst_datas[@intFromEnum(inst)].un_op;
src/Liveness/Verify.zig+1
...@@ -151,6 +151,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -151,6 +151,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
151 try self.verifyInstOperands(inst, .{ un_op, .none, .none });151 try self.verifyInstOperands(inst, .{ un_op, .none, .none });
152 },152 },
153 .ret,153 .ret,
154 .ret_safe,
154 .ret_load,155 .ret_load,
155 => {156 => {
156 const un_op = data[@intFromEnum(inst)].un_op;157 const un_op = data[@intFromEnum(inst)].un_op;
src/Sema.zig+3-2
...@@ -19437,14 +19437,15 @@ fn analyzeRet(...@@ -19437,14 +19437,15 @@ fn analyzeRet(
1943719437
19438 try sema.resolveTypeLayout(sema.fn_ret_ty);19438 try sema.resolveTypeLayout(sema.fn_ret_ty);
1943919439
19440 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret;
19440 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {19441 if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
19441 // Avoid adding a frame to the error return trace in case the value is comptime-known19442 // Avoid adding a frame to the error return trace in case the value is comptime-known
19442 // to be not an error.19443 // to be not an error.
19443 const is_non_err = try sema.analyzeIsNonErr(block, src, operand);19444 const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
19444 return sema.retWithErrTracing(block, src, is_non_err, .ret, operand);19445 return sema.retWithErrTracing(block, src, is_non_err, air_tag, operand);
19445 }19446 }
1944619447
19447 _ = try block.addUnOp(.ret, operand);19448 _ = try block.addUnOp(air_tag, operand);
1944819449
19449 return always_noreturn;19450 return always_noreturn;
19450}19451}
src/arch/aarch64/CodeGen.zig+1
...@@ -766,6 +766,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -766,6 +766,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
766 .not => try self.airNot(inst),766 .not => try self.airNot(inst),
767 .int_from_ptr => try self.airIntFromPtr(inst),767 .int_from_ptr => try self.airIntFromPtr(inst),
768 .ret => try self.airRet(inst),768 .ret => try self.airRet(inst),
769 .ret_safe => try self.airRet(inst), // TODO
769 .ret_load => try self.airRetLoad(inst),770 .ret_load => try self.airRetLoad(inst),
770 .store => try self.airStore(inst, false),771 .store => try self.airStore(inst, false),
771 .store_safe => try self.airStore(inst, true),772 .store_safe => try self.airStore(inst, true),
src/arch/arm/CodeGen.zig+1
...@@ -752,6 +752,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -752,6 +752,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
752 .not => try self.airNot(inst),752 .not => try self.airNot(inst),
753 .int_from_ptr => try self.airIntFromPtr(inst),753 .int_from_ptr => try self.airIntFromPtr(inst),
754 .ret => try self.airRet(inst),754 .ret => try self.airRet(inst),
755 .ret_safe => try self.airRet(inst), // TODO
755 .ret_load => try self.airRetLoad(inst),756 .ret_load => try self.airRetLoad(inst),
756 .store => try self.airStore(inst, false),757 .store => try self.airStore(inst, false),
757 .store_safe => try self.airStore(inst, true),758 .store_safe => try self.airStore(inst, true),
src/arch/riscv64/CodeGen.zig+1
...@@ -585,6 +585,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -585,6 +585,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
585 .not => try self.airNot(inst),585 .not => try self.airNot(inst),
586 .int_from_ptr => try self.airIntFromPtr(inst),586 .int_from_ptr => try self.airIntFromPtr(inst),
587 .ret => try self.airRet(inst),587 .ret => try self.airRet(inst),
588 .ret_safe => try self.airRet(inst), // TODO
588 .ret_load => try self.airRetLoad(inst),589 .ret_load => try self.airRetLoad(inst),
589 .store => try self.airStore(inst, false),590 .store => try self.airStore(inst, false),
590 .store_safe => try self.airStore(inst, true),591 .store_safe => try self.airStore(inst, true),
src/arch/sparc64/CodeGen.zig+1
...@@ -598,6 +598,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -598,6 +598,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
598 .not => try self.airNot(inst),598 .not => try self.airNot(inst),
599 .int_from_ptr => try self.airIntFromPtr(inst),599 .int_from_ptr => try self.airIntFromPtr(inst),
600 .ret => try self.airRet(inst),600 .ret => try self.airRet(inst),
601 .ret_safe => try self.airRet(inst), // TODO
601 .ret_load => try self.airRetLoad(inst),602 .ret_load => try self.airRetLoad(inst),
602 .store => try self.airStore(inst, false),603 .store => try self.airStore(inst, false),
603 .store_safe => try self.airStore(inst, true),604 .store_safe => try self.airStore(inst, true),
src/arch/wasm/CodeGen.zig+1
...@@ -1957,6 +1957,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1957,6 +1957,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1957 .ptr_elem_val => func.airPtrElemVal(inst),1957 .ptr_elem_val => func.airPtrElemVal(inst),
1958 .int_from_ptr => func.airIntFromPtr(inst),1958 .int_from_ptr => func.airIntFromPtr(inst),
1959 .ret => func.airRet(inst),1959 .ret => func.airRet(inst),
1960 .ret_safe => func.airRet(inst), // TODO
1960 .ret_ptr => func.airRetPtr(inst),1961 .ret_ptr => func.airRetPtr(inst),
1961 .ret_load => func.airRetLoad(inst),1962 .ret_load => func.airRetLoad(inst),
1962 .splat => func.airSplat(inst),1963 .splat => func.airSplat(inst),
src/arch/x86_64/CodeGen.zig+1
...@@ -2018,6 +2018,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -2018,6 +2018,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
2018 .loop => try self.airLoop(inst),2018 .loop => try self.airLoop(inst),
2019 .int_from_ptr => try self.airIntFromPtr(inst),2019 .int_from_ptr => try self.airIntFromPtr(inst),
2020 .ret => try self.airRet(inst),2020 .ret => try self.airRet(inst),
2021 .ret_safe => try self.airRet(inst), // TODO
2021 .ret_load => try self.airRetLoad(inst),2022 .ret_load => try self.airRetLoad(inst),
2022 .store => try self.airStore(inst, false),2023 .store => try self.airStore(inst, false),
2023 .store_safe => try self.airStore(inst, true),2024 .store_safe => try self.airStore(inst, true),
src/codegen/c.zig+1
...@@ -3225,6 +3225,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -3225,6 +3225,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
3225 .int_from_bool => try airIntFromBool(f, inst),3225 .int_from_bool => try airIntFromBool(f, inst),
3226 .load => try airLoad(f, inst),3226 .load => try airLoad(f, inst),
3227 .ret => try airRet(f, inst, false),3227 .ret => try airRet(f, inst, false),
3228 .ret_safe => try airRet(f, inst, false), // TODO
3228 .ret_load => try airRet(f, inst, true),3229 .ret_load => try airRet(f, inst, true),
3229 .store => try airStore(f, inst, false),3230 .store => try airStore(f, inst, false),
3230 .store_safe => try airStore(f, inst, true),3231 .store_safe => try airStore(f, inst, true),
src/codegen/llvm.zig+51-3
...@@ -5078,7 +5078,8 @@ pub const FuncGen = struct {...@@ -5078,7 +5078,8 @@ pub const FuncGen = struct {
5078 .load => try self.airLoad(body[i..]),5078 .load => try self.airLoad(body[i..]),
5079 .loop => try self.airLoop(inst),5079 .loop => try self.airLoop(inst),
5080 .not => try self.airNot(inst),5080 .not => try self.airNot(inst),
5081 .ret => try self.airRet(inst),5081 .ret => try self.airRet(inst, false),
5082 .ret_safe => try self.airRet(inst, true),
5082 .ret_load => try self.airRetLoad(inst),5083 .ret_load => try self.airRetLoad(inst),
5083 .store => try self.airStore(inst, false),5084 .store => try self.airStore(inst, false),
5084 .store_safe => try self.airStore(inst, true),5085 .store_safe => try self.airStore(inst, true),
...@@ -5551,15 +5552,42 @@ pub const FuncGen = struct {...@@ -5551,15 +5552,42 @@ pub const FuncGen = struct {
5551 _ = try fg.wip.@"unreachable"();5552 _ = try fg.wip.@"unreachable"();
5552 }5553 }
55535554
5554 fn airRet(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {5555 fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !Builder.Value {
5555 const o = self.dg.object;5556 const o = self.dg.object;
5556 const mod = o.module;5557 const mod = o.module;
5557 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5558 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
5558 const ret_ty = self.typeOf(un_op);5559 const ret_ty = self.typeOf(un_op);
5560
5559 if (self.ret_ptr != .none) {5561 if (self.ret_ptr != .none) {
5560 const operand = try self.resolveInst(un_op);
5561 const ptr_ty = try mod.singleMutPtrType(ret_ty);5562 const ptr_ty = try mod.singleMutPtrType(ret_ty);
55625563
5564 const operand = try self.resolveInst(un_op);
5565 const val_is_undef = if (try self.air.value(un_op, mod)) |val| val.isUndefDeep(mod) else false;
5566 if (val_is_undef and safety) undef: {
5567 const ptr_info = ptr_ty.ptrInfo(mod);
5568 const needs_bitmask = (ptr_info.packed_offset.host_size != 0);
5569 if (needs_bitmask) {
5570 // TODO: only some bits are to be undef, we cannot write with a simple memset.
5571 // meanwhile, ignore the write rather than stomping over valid bits.
5572 // https://github.com/ziglang/zig/issues/15337
5573 break :undef;
5574 }
5575 const len = try o.builder.intValue(try o.lowerType(Type.usize), ret_ty.abiSize(mod));
5576 _ = try self.wip.callMemSet(
5577 self.ret_ptr,
5578 ptr_ty.ptrAlignment(mod).toLlvm(),
5579 try o.builder.intValue(.i8, 0xaa),
5580 len,
5581 if (ptr_ty.isVolatilePtr(mod)) .@"volatile" else .normal,
5582 );
5583 const owner_mod = self.dg.ownerModule();
5584 if (owner_mod.valgrind) {
5585 try self.valgrindMarkUndef(self.ret_ptr, len);
5586 }
5587 _ = try self.wip.retVoid();
5588 return .none;
5589 }
5590
5563 const unwrapped_operand = operand.unwrap();5591 const unwrapped_operand = operand.unwrap();
5564 const unwrapped_ret = self.ret_ptr.unwrap();5592 const unwrapped_ret = self.ret_ptr.unwrap();
55655593
...@@ -5588,8 +5616,28 @@ pub const FuncGen = struct {...@@ -5588,8 +5616,28 @@ pub const FuncGen = struct {
55885616
5589 const abi_ret_ty = try lowerFnRetTy(o, fn_info);5617 const abi_ret_ty = try lowerFnRetTy(o, fn_info);
5590 const operand = try self.resolveInst(un_op);5618 const operand = try self.resolveInst(un_op);
5619 const val_is_undef = if (try self.air.value(un_op, mod)) |val| val.isUndefDeep(mod) else false;
5591 const alignment = ret_ty.abiAlignment(mod).toLlvm();5620 const alignment = ret_ty.abiAlignment(mod).toLlvm();
55925621
5622 if (val_is_undef and safety) {
5623 const llvm_ret_ty = operand.typeOfWip(&self.wip);
5624 const rp = try self.buildAlloca(llvm_ret_ty, alignment);
5625 const len = try o.builder.intValue(try o.lowerType(Type.usize), ret_ty.abiSize(mod));
5626 _ = try self.wip.callMemSet(
5627 rp,
5628 alignment,
5629 try o.builder.intValue(.i8, 0xaa),
5630 len,
5631 .normal,
5632 );
5633 const owner_mod = self.dg.ownerModule();
5634 if (owner_mod.valgrind) {
5635 try self.valgrindMarkUndef(rp, len);
5636 }
5637 _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, rp, alignment, ""));
5638 return .none;
5639 }
5640
5593 if (isByRef(ret_ty, mod)) {5641 if (isByRef(ret_ty, mod)) {
5594 // operand is a pointer however self.ret_ptr is null so that means5642 // operand is a pointer however self.ret_ptr is null so that means
5595 // we need to return a value.5643 // we need to return a value.
src/codegen/spirv.zig+1
...@@ -2177,6 +2177,7 @@ const DeclGen = struct {...@@ -2177,6 +2177,7 @@ const DeclGen = struct {
2177 .cond_br => return self.airCondBr(inst),2177 .cond_br => return self.airCondBr(inst),
2178 .loop => return self.airLoop(inst),2178 .loop => return self.airLoop(inst),
2179 .ret => return self.airRet(inst),2179 .ret => return self.airRet(inst),
2180 .ret_safe => return self.airRet(inst), // TODO
2180 .ret_load => return self.airRetLoad(inst),2181 .ret_load => return self.airRetLoad(inst),
2181 .@"try" => try self.airTry(inst),2182 .@"try" => try self.airTry(inst),
2182 .switch_br => return self.airSwitchBr(inst),2183 .switch_br => return self.airSwitchBr(inst),
src/print_air.zig+1
...@@ -175,6 +175,7 @@ const Writer = struct {...@@ -175,6 +175,7 @@ const Writer = struct {
175 .int_from_ptr,175 .int_from_ptr,
176 .int_from_bool,176 .int_from_bool,
177 .ret,177 .ret,
178 .ret_safe,
178 .ret_load,179 .ret_load,
179 .is_named_enum_value,180 .is_named_enum_value,
180 .tag_name,181 .tag_name,
test/behavior/undefined.zig+22
...@@ -97,3 +97,25 @@ test "reslice of undefined global var slice" {...@@ -97,3 +97,25 @@ test "reslice of undefined global var slice" {
97 const x = buf[0..1];97 const x = buf[0..1];
98 try @import("std").testing.expect(x.len == 1 and x[0] == 0);98 try @import("std").testing.expect(x.len == 1 and x[0] == 0);
99}99}
100
101test "returned undef is 0xaa bytes when runtime safety is enabled" {
102 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
104 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
105 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
106 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
107
108 const Rect = struct {
109 x: f32,
110 fn getUndefStruct() @This() {
111 @setRuntimeSafety(true);
112 return undefined;
113 }
114 fn getUndefInt() u32 {
115 @setRuntimeSafety(true);
116 return undefined;
117 }
118 };
119 try std.testing.expect(@as(u32, @bitCast(Rect.getUndefStruct().x)) == 0xAAAAAAAA);
120 try std.testing.expect(Rect.getUndefInt() == 0xAAAAAAAA);
121}