authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-25 16:10:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-25 16:15:48-07:00
logd2ad8afff4c404f6e1a566cce3fa6e7f768503e5
tree28ca64fb5e60cf15fdff8a4c6be921c83b7ce8b1
parent9d231c4991ec1e33c3f4a96e5941848705ef5050

LLVM: fix missing alignment on wrapping instructions

Previously, when lowering AIR instructions `wrap_errunion_payload`, `wrap_errunion_err`, and `wrap_optional`, the LLVM backend would create an alloca instruction to store the result, but did not set the alignment on it. This caused UB which went undetected for a long time until we started enabling the stack protector. Closes #12594 Unblocks #12508 Inspires #12634 Tests passed locally: * test-behavior * test-cases

3 files changed, 39 insertions(+), 3 deletions(-)

src/codegen/llvm.zig+7-3
...@@ -6188,7 +6188,9 @@ pub const FuncGen = struct {...@@ -6188,7 +6188,9 @@ pub const FuncGen = struct {
6188 }6188 }
6189 const llvm_optional_ty = try self.dg.lowerType(optional_ty);6189 const llvm_optional_ty = try self.dg.lowerType(optional_ty);
6190 if (isByRef(optional_ty)) {6190 if (isByRef(optional_ty)) {
6191 const target = self.dg.module.getTarget();
6191 const optional_ptr = self.buildAlloca(llvm_optional_ty);6192 const optional_ptr = self.buildAlloca(llvm_optional_ty);
6193 optional_ptr.setAlignment(optional_ty.abiAlignment(target));
6192 const payload_ptr = self.builder.buildStructGEP(optional_ptr, 0, "");6194 const payload_ptr = self.builder.buildStructGEP(optional_ptr, 0, "");
6193 var ptr_ty_payload: Type.Payload.ElemType = .{6195 var ptr_ty_payload: Type.Payload.ElemType = .{
6194 .base = .{ .tag = .single_mut_pointer },6196 .base = .{ .tag = .single_mut_pointer },
...@@ -6208,20 +6210,21 @@ pub const FuncGen = struct {...@@ -6208,20 +6210,21 @@ pub const FuncGen = struct {
6208 if (self.liveness.isUnused(inst)) return null;6210 if (self.liveness.isUnused(inst)) return null;
62096211
6210 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6212 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6211 const inst_ty = self.air.typeOfIndex(inst);6213 const err_un_ty = self.air.typeOfIndex(inst);
6212 const operand = try self.resolveInst(ty_op.operand);6214 const operand = try self.resolveInst(ty_op.operand);
6213 const payload_ty = self.air.typeOf(ty_op.operand);6215 const payload_ty = self.air.typeOf(ty_op.operand);
6214 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {6216 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
6215 return operand;6217 return operand;
6216 }6218 }
6217 const ok_err_code = (try self.dg.lowerType(Type.anyerror)).constNull();6219 const ok_err_code = (try self.dg.lowerType(Type.anyerror)).constNull();
6218 const err_un_llvm_ty = try self.dg.lowerType(inst_ty);6220 const err_un_llvm_ty = try self.dg.lowerType(err_un_ty);
62196221
6220 const target = self.dg.module.getTarget();6222 const target = self.dg.module.getTarget();
6221 const payload_offset = errUnionPayloadOffset(payload_ty, target);6223 const payload_offset = errUnionPayloadOffset(payload_ty, target);
6222 const error_offset = errUnionErrorOffset(payload_ty, target);6224 const error_offset = errUnionErrorOffset(payload_ty, target);
6223 if (isByRef(inst_ty)) {6225 if (isByRef(err_un_ty)) {
6224 const result_ptr = self.buildAlloca(err_un_llvm_ty);6226 const result_ptr = self.buildAlloca(err_un_llvm_ty);
6227 result_ptr.setAlignment(err_un_ty.abiAlignment(target));
6225 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");6228 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");
6226 const store_inst = self.builder.buildStore(ok_err_code, err_ptr);6229 const store_inst = self.builder.buildStore(ok_err_code, err_ptr);
6227 store_inst.setAlignment(Type.anyerror.abiAlignment(target));6230 store_inst.setAlignment(Type.anyerror.abiAlignment(target));
...@@ -6256,6 +6259,7 @@ pub const FuncGen = struct {...@@ -6256,6 +6259,7 @@ pub const FuncGen = struct {
6256 const error_offset = errUnionErrorOffset(payload_ty, target);6259 const error_offset = errUnionErrorOffset(payload_ty, target);
6257 if (isByRef(err_un_ty)) {6260 if (isByRef(err_un_ty)) {
6258 const result_ptr = self.buildAlloca(err_un_llvm_ty);6261 const result_ptr = self.buildAlloca(err_un_llvm_ty);
6262 result_ptr.setAlignment(err_un_ty.abiAlignment(target));
6259 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");6263 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");
6260 const store_inst = self.builder.buildStore(operand, err_ptr);6264 const store_inst = self.builder.buildStore(operand, err_ptr);
6261 store_inst.setAlignment(Type.anyerror.abiAlignment(target));6265 store_inst.setAlignment(Type.anyerror.abiAlignment(target));
test/behavior/error.zig+16
...@@ -796,3 +796,19 @@ test "error union of noreturn used with catch" {...@@ -796,3 +796,19 @@ test "error union of noreturn used with catch" {
796 const err = NoReturn.testCatch();796 const err = NoReturn.testCatch();
797 try expect(err == error.OtherFailure);797 try expect(err == error.OtherFailure);
798}798}
799
800test "alignment of wrapping an error union payload" {
801 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
802 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
803 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
804
805 const S = struct {
806 const I = extern struct { x: i128 };
807
808 fn foo() anyerror!I {
809 var i: I = .{ .x = 1234 };
810 return i;
811 }
812 };
813 try expect((S.foo() catch unreachable).x == 1234);
814}
test/behavior/optional.zig+16
...@@ -412,3 +412,19 @@ test "orelse on C pointer" {...@@ -412,3 +412,19 @@ test "orelse on C pointer" {
412 const d = foo orelse @compileError("bad");412 const d = foo orelse @compileError("bad");
413 try expectEqual([*c]const u8, @TypeOf(d));413 try expectEqual([*c]const u8, @TypeOf(d));
414}414}
415
416test "alignment of wrapping an optional payload" {
417 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
418 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
419 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
420
421 const S = struct {
422 const I = extern struct { x: i128 };
423
424 fn foo() ?I {
425 var i: I = .{ .x = 1234 };
426 return i;
427 }
428 };
429 try expect(S.foo().?.x == 1234);
430}