From e6ebdcb82ed9fd226ef18670115b8b2617c0a392 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 10 Oct 2022 23:57:48 -0700 Subject: [PATCH] stage2 LLVM: Use a packed aggregate for union payload init Without the packed qualifier, the type layout that we use to initialize doesn't match the correct layout of the underlying storage, causing corrupted data and past-the-end writes. --- src/codegen/llvm.zig | 4 ++-- test/behavior.zig | 1 + test/behavior/bugs/13128.zig | 29 +++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 test/behavior/bugs/13128.zig diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a24c14c9c6..300a24280a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -8897,7 +8897,7 @@ pub const FuncGen = struct { return union_llvm_ty.constInt(tag_int, .False); } assert(isByRef(union_ty)); - // The llvm type of the alloca will the the named LLVM union type, which will not + // The llvm type of the alloca will be the named LLVM union type, and will not // necessarily match the format that we need, depending on which tag is active. We // must construct the correct unnamed struct type here and bitcast, in order to // then set the fields appropriately. @@ -8922,7 +8922,7 @@ pub const FuncGen = struct { const fields: [2]*llvm.Type = .{ field_llvm_ty, self.context.intType(8).arrayType(padding_len), }; - break :p self.context.structType(&fields, fields.len, .False); + break :p self.context.structType(&fields, fields.len, .True); }; if (layout.tag_size == 0) { const fields: [1]*llvm.Type = .{payload}; diff --git a/test/behavior.zig b/test/behavior.zig index 6c181d38bb..6a6844a840 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -100,6 +100,7 @@ test { _ = @import("behavior/bugs/12928.zig"); _ = @import("behavior/bugs/12945.zig"); _ = @import("behavior/bugs/12984.zig"); + _ = @import("behavior/bugs/13128.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); diff --git a/test/behavior/bugs/13128.zig b/test/behavior/bugs/13128.zig new file mode 100644 index 0000000000..057270a96f --- /dev/null +++ b/test/behavior/bugs/13128.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const expect = std.testing.expect; + +const U = union(enum) { + x: u128, + y: [17]u8, +}; + +fn foo(val: U) !void { + try expect(val.x == 1); +} + +test "runtime union init, most-aligned field != largest" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + var x: u8 = 1; + try foo(.{ .x = x }); + + const val: U = @unionInit(U, "x", x); + try expect(val.x == 1); + + const val2: U = .{ .x = x }; + try expect(val2.x == 1); +}