From 4ec299007a6a183edddfcb0505a9c1501da7ad0c Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 16 Mar 2023 17:51:31 +0000 Subject: [PATCH 1/7] Sema: allow dereferencing ill-defined pointers to zero-bit types at comptime It doesn't matter if a pointer to a zero-bit (i.e. OPV) type is undefined or runtime-known; we still know the result of the dereference at comptime. Code may use this, for instance, when allocating zero-bit types: `@as(*void, undefined)` is entirely reasonable to use at runtime, since we know the pointer will never be accessed, thus it should be valid at comptime too. --- src/Sema.zig | 5 +++++ test/behavior/comptime_memory.zig | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 8b476d4542..cdbd8f8b41 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4712,6 +4712,11 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(sema.mod)}), } + if ((try sema.typeHasOnePossibleValue(operand_ty.childType())) != null) { + // No need to validate the actual pointer value, we don't need it! + return; + } + const elem_ty = operand_ty.elemType2(); if (try sema.resolveMaybeUndefVal(operand)) |val| { if (val.isUndef()) { diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index 71d177395b..98e6e65725 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -420,3 +420,11 @@ test "mutate entire slice at comptime" { buf[1..3].* = x; } } + +test "dereference undefined pointer to zero-bit type" { + const p0: *void = undefined; + try testing.expectEqual({}, p0.*); + + const p1: *[0]u32 = undefined; + try testing.expect(p1.*.len == 0); +} From cfcd6698cd9385938df27a2052c2309229171e4f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 16 Mar 2023 01:32:43 -0400 Subject: [PATCH 2/7] main: add debug option to dump unoptimized llvm ir --- lib/build_runner.zig | 9 +++++++-- lib/std/Build.zig | 7 +++++-- lib/std/Build/CompileStep.zig | 3 ++- src/Compilation.zig | 9 +++++++-- src/Module.zig | 4 ++-- src/codegen/llvm.zig | 31 +++++++++++++++++++++++++++++-- src/codegen/llvm/bindings.zig | 3 +++ src/glibc.zig | 1 + src/libcxx.zig | 2 ++ src/libtsan.zig | 1 + src/libunwind.zig | 1 + src/main.zig | 23 +++++++++++++++-------- 12 files changed, 75 insertions(+), 19 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 5f5601a68d..56dcf43155 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -204,7 +204,11 @@ pub fn main() !void { } else if (mem.eql(u8, arg, "--verbose-air")) { builder.verbose_air = true; } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { - builder.verbose_llvm_ir = true; + builder.verbose_llvm_ir = "-"; + } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) { + builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..]; + } else if (mem.eql(u8, arg, "--verbose-llvm-bc=")) { + builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..]; } else if (mem.eql(u8, arg, "--verbose-cimport")) { builder.verbose_cimport = true; } else if (mem.eql(u8, arg, "--verbose-cc")) { @@ -990,7 +994,8 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\ --debug-pkg-config Fail if unknown pkg-config flags encountered \\ --verbose-link Enable compiler debug output for linking \\ --verbose-air Enable compiler debug output for Zig AIR - \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR + \\ --verbose-llvm-ir[=file] Enable compiler debug output for LLVM IR + \\ --verbose-llvm-bc=[file] Enable compiler debug output for LLVM BC \\ --verbose-cimport Enable compiler debug output for C imports \\ --verbose-cc Enable compiler debug output for C compilation \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 279dd765b5..632a45cf23 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -54,7 +54,8 @@ verbose: bool, verbose_link: bool, verbose_cc: bool, verbose_air: bool, -verbose_llvm_ir: bool, +verbose_llvm_ir: ?[]const u8, +verbose_llvm_bc: ?[]const u8, verbose_cimport: bool, verbose_llvm_cpu_features: bool, reference_trace: ?u32 = null, @@ -204,7 +205,8 @@ pub fn create( .verbose_link = false, .verbose_cc = false, .verbose_air = false, - .verbose_llvm_ir = false, + .verbose_llvm_ir = null, + .verbose_llvm_bc = null, .verbose_cimport = false, .verbose_llvm_cpu_features = false, .invalid_user_input = false, @@ -292,6 +294,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc .verbose_cc = parent.verbose_cc, .verbose_air = parent.verbose_air, .verbose_llvm_ir = parent.verbose_llvm_ir, + .verbose_llvm_bc = parent.verbose_llvm_bc, .verbose_cimport = parent.verbose_cimport, .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, .reference_trace = parent.reference_trace, diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index d73e5d3b41..72855f360e 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -1438,7 +1438,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (b.verbose_cimport) try zig_args.append("--verbose-cimport"); if (b.verbose_air) try zig_args.append("--verbose-air"); - if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); + if (b.verbose_llvm_ir) |path| try zig_args.append(b.fmt("--verbose-llvm-ir={s}", .{path})); + if (b.verbose_llvm_bc) |path| try zig_args.append(b.fmt("--verbose-llvm-bc={s}", .{path})); if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); diff --git a/src/Compilation.zig b/src/Compilation.zig index 89512ce744..858afb6ca3 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -86,7 +86,8 @@ clang_preprocessor_mode: ClangPreprocessorMode, /// Whether to print clang argvs to stdout. verbose_cc: bool, verbose_air: bool, -verbose_llvm_ir: bool, +verbose_llvm_ir: ?[]const u8, +verbose_llvm_bc: ?[]const u8, verbose_cimport: bool, verbose_llvm_cpu_features: bool, disable_c_depfile: bool, @@ -585,7 +586,8 @@ pub const InitOptions = struct { verbose_cc: bool = false, verbose_link: bool = false, verbose_air: bool = false, - verbose_llvm_ir: bool = false, + verbose_llvm_ir: ?[]const u8 = null, + verbose_llvm_bc: ?[]const u8 = null, verbose_cimport: bool = false, verbose_llvm_cpu_features: bool = false, is_test: bool = false, @@ -1559,6 +1561,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .verbose_cc = options.verbose_cc, .verbose_air = options.verbose_air, .verbose_llvm_ir = options.verbose_llvm_ir, + .verbose_llvm_bc = options.verbose_llvm_bc, .verbose_cimport = options.verbose_cimport, .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, .disable_c_depfile = options.disable_c_depfile, @@ -5342,6 +5345,7 @@ fn buildOutputFromZig( .verbose_link = comp.bin_file.options.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, @@ -5419,6 +5423,7 @@ pub fn build_crt_file( .verbose_link = comp.bin_file.options.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, diff --git a/src/Module.zig b/src/Module.zig index c47e4fc234..17016865d1 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4263,7 +4263,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { comp.emit_llvm_bc == null); const dump_air = builtin.mode == .Debug and comp.verbose_air; - const dump_llvm_ir = builtin.mode == .Debug and comp.verbose_llvm_ir; + const dump_llvm_ir = builtin.mode == .Debug and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); if (no_bin_file and !dump_air and !dump_llvm_ir) return; @@ -6395,7 +6395,7 @@ pub fn linkerUpdateDecl(mod: *Module, decl_index: Decl.Index) !void { comp.emit_llvm_ir == null and comp.emit_llvm_bc == null); - const dump_llvm_ir = builtin.mode == .Debug and comp.verbose_llvm_ir; + const dump_llvm_ir = builtin.mode == .Debug and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); if (no_bin_file and !dump_llvm_ir) return; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 85a82f4eda..11fc44747e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -751,8 +751,35 @@ pub const Object = struct { dib.finalize(); } - if (comp.verbose_llvm_ir) { - self.llvm_module.dump(); + if (comp.verbose_llvm_ir) |path| { + if (std.mem.eql(u8, path, "-")) { + self.llvm_module.dump(); + } else { + const path_z = try comp.gpa.dupeZ(u8, path); + defer comp.gpa.free(path_z); + + var error_message: [*:0]const u8 = undefined; + + if (self.llvm_module.printModuleToFile(path_z, &error_message).toBool()) { + defer llvm.disposeMessage(error_message); + + log.err("dump LLVM module failed ir={s}: {s}", .{ + path, error_message, + }); + } + } + } + + if (comp.verbose_llvm_bc) |path| { + const path_z = try comp.gpa.dupeZ(u8, path); + defer comp.gpa.free(path_z); + + const error_code = self.llvm_module.writeBitcodeToFile(path_z); + if (error_code != 0) { + log.err("dump LLVM module failed bc={s}: {d}", .{ + path, error_code, + }); + } } var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index e16da29335..c759c7874b 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -422,6 +422,9 @@ pub const Module = opaque { pub const printModuleToFile = LLVMPrintModuleToFile; extern fn LLVMPrintModuleToFile(M: *Module, Filename: [*:0]const u8, ErrorMessage: *[*:0]const u8) Bool; + + pub const writeBitcodeToFile = LLVMWriteBitcodeToFile; + extern fn LLVMWriteBitcodeToFile(M: *Module, Path: [*:0]const u8) c_int; }; pub const lookupIntrinsicID = LLVMLookupIntrinsicID; diff --git a/src/glibc.zig b/src/glibc.zig index b37398bffd..327e4f4bb9 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -1097,6 +1097,7 @@ fn buildSharedLib( .verbose_link = comp.bin_file.options.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, diff --git a/src/libcxx.zig b/src/libcxx.zig index 9c5dc9426f..07e3473338 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -250,6 +250,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { .verbose_link = comp.bin_file.options.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, @@ -410,6 +411,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { .verbose_link = comp.bin_file.options.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, diff --git a/src/libtsan.zig b/src/libtsan.zig index 54bf00e4b6..90d2537876 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -226,6 +226,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { .verbose_link = comp.bin_file.options.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, diff --git a/src/libunwind.zig b/src/libunwind.zig index aefbfb457d..6c46b6bb28 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -122,6 +122,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { .verbose_link = comp.bin_file.options.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, diff --git a/src/main.zig b/src/main.zig index e7d5c647b5..e28bdf34cf 100644 --- a/src/main.zig +++ b/src/main.zig @@ -370,10 +370,10 @@ const usage_build_generic = \\ -fno-emit-bin Do not output machine code \\ -femit-asm[=path] Output .s (assembly code) \\ -fno-emit-asm (default) Do not output .s (assembly code) - \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions) - \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR - \\ -femit-llvm-bc[=path] Produce a LLVM module as a .bc file (requires LLVM extensions) - \\ -fno-emit-llvm-bc (default) Do not produce a LLVM module as a .bc file + \\ -femit-llvm-ir[=path] Produce a .ll file with optimized LLVM IR (requires LLVM extensions) + \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with optimized LLVM IR + \\ -femit-llvm-bc[=path] Produce an optimized LLVM module as a .bc file (requires LLVM extensions) + \\ -fno-emit-llvm-bc (default) Do not produce an optimized LLVM module as a .bc file \\ -femit-h[=path] Generate a C header file (.h) \\ -fno-emit-h (default) Do not generate a C header file (.h) \\ -femit-docs[=path] Create a docs/ dir with html documentation @@ -555,13 +555,14 @@ const usage_build_generic = \\ --test-runner [path] Specify a custom test runner \\ \\Debug Options (Zig Compiler Development): - \\ -fopt-bisect-limit [limit] Only run [limit] first LLVM optimization passes + \\ -fopt-bisect-limit=[limit] Only run [limit] first LLVM optimization passes \\ -ftime-report Print timing diagnostics \\ -fstack-report Print stack size diagnostics \\ --verbose-link Display linker invocations \\ --verbose-cc Display C compiler invocations \\ --verbose-air Enable compiler debug output for Zig AIR - \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR + \\ --verbose-llvm-ir[=path] Enable compiler debug output for unoptimized LLVM IR + \\ --verbose-llvm-bc=[path] Enable compiler debug output for unoptimized LLVM BC \\ --verbose-cimport Enable compiler debug output for C imports \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features \\ --debug-log [scope] Enable printing debug/info log messages for scope @@ -704,7 +705,8 @@ fn buildOutputType( var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); var verbose_air = false; - var verbose_llvm_ir = false; + var verbose_llvm_ir: ?[]const u8 = null; + var verbose_llvm_bc: ?[]const u8 = null; var verbose_cimport = false; var verbose_llvm_cpu_features = false; var time_report = false; @@ -1441,7 +1443,11 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--verbose-air")) { verbose_air = true; } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { - verbose_llvm_ir = true; + verbose_llvm_ir = "-"; + } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) { + verbose_llvm_ir = arg["--verbose-llvm-ir=".len..]; + } else if (mem.startsWith(u8, arg, "--verbose-llvm-bc=")) { + verbose_llvm_bc = arg["--verbose-llvm-bc=".len..]; } else if (mem.eql(u8, arg, "--verbose-cimport")) { verbose_cimport = true; } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { @@ -3226,6 +3232,7 @@ fn buildOutputType( .verbose_link = verbose_link, .verbose_air = verbose_air, .verbose_llvm_ir = verbose_llvm_ir, + .verbose_llvm_bc = verbose_llvm_bc, .verbose_cimport = verbose_cimport, .verbose_llvm_cpu_features = verbose_llvm_cpu_features, .machine_code_model = machine_code_model, From e0dd20b02ea026e125b36b3674f20aa20ff043e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 17 Mar 2023 18:50:25 +0200 Subject: [PATCH 3/7] add BoundedArrayAligned (#14580) This is useful for creating byte buffers of actually-different-things. Copied the argument order from `Allocator.alignedAlloc` I noted that `ArrayListAligned` is going out of it's way to not set the alignment at comptime when it is not specified. However, I was not able to do that the same way here, and good people on IRC, @ifreund in particular (thanks!) assured me that [N]T align(@alignOf(T)) is equivalent to [N]T --- lib/std/bounded_array.zig | 45 +++++++++++++++++++++++++++++++++------ lib/std/std.zig | 1 + 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig index 5242470631..8a4c2db64b 100644 --- a/lib/std/bounded_array.zig +++ b/lib/std/bounded_array.zig @@ -16,9 +16,30 @@ const testing = std.testing; /// var a_clone = a; // creates a copy - the structure doesn't use any internal pointers /// ``` pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { + return BoundedArrayAligned(T, @alignOf(T), buffer_capacity); +} + +/// A structure with an array, length and alignment, that can be used as a +/// slice. +/// +/// Useful to pass around small explicitly-aligned arrays whose exact size is +/// only known at runtime, but whose maximum size is known at comptime, without +/// requiring an `Allocator`. +/// ```zig +// var a = try BoundedArrayAligned(u8, 16, 2).init(0); +// try a.append(255); +// try a.append(255); +// const b = @ptrCast(*const [1]u16, a.constSlice().ptr); +// try testing.expectEqual(@as(u16, 65535), b[0]); +/// ``` +pub fn BoundedArrayAligned( + comptime T: type, + comptime alignment: u29, + comptime buffer_capacity: usize, +) type { return struct { const Self = @This(); - buffer: [buffer_capacity]T = undefined, + buffer: [buffer_capacity]T align(alignment) = undefined, len: usize = 0, /// Set the actual length of the slice. @@ -30,15 +51,15 @@ pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { /// View the internal array as a slice whose size was previously set. pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) { - *[buffer_capacity]T => []T, - *const [buffer_capacity]T => []const T, + *align(alignment) [buffer_capacity]T => []align(alignment) T, + *align(alignment) const [buffer_capacity]T => []align(alignment) const T, else => unreachable, } { return self.buffer[0..self.len]; } /// View the internal array as a constant slice whose size was previously set. - pub fn constSlice(self: *const Self) []const T { + pub fn constSlice(self: *const Self) []align(alignment) const T { return self.slice(); } @@ -94,7 +115,7 @@ pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { /// Resize the slice, adding `n` new elements, which have `undefined` values. /// The return value is a slice pointing to the uninitialized elements. - pub fn addManyAsArray(self: *Self, comptime n: usize) error{Overflow}!*[n]T { + pub fn addManyAsArray(self: *Self, comptime n: usize) error{Overflow}!*align(alignment) [n]T { const prev_len = self.len; try self.resize(self.len + n); return self.slice()[prev_len..][0..n]; @@ -118,7 +139,7 @@ pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { /// This can be useful for writing directly into it. /// Note that such an operation must be followed up with a /// call to `resize()` - pub fn unusedCapacitySlice(self: *Self) []T { + pub fn unusedCapacitySlice(self: *Self) []align(alignment) T { return self.buffer[self.len..]; } @@ -365,3 +386,15 @@ test "BoundedArray" { try w.writeAll(s); try testing.expectEqualStrings(s, a.constSlice()); } + +test "BoundedArrayAligned" { + var a = try BoundedArrayAligned(u8, 16, 4).init(0); + try a.append(0); + try a.append(0); + try a.append(255); + try a.append(255); + + const b = @ptrCast(*const [2]u16, a.constSlice().ptr); + try testing.expectEqual(@as(u16, 0), b[0]); + try testing.expectEqual(@as(u16, 65535), b[1]); +} diff --git a/lib/std/std.zig b/lib/std/std.zig index 92ebdf595b..ef4a6831d0 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged; pub const AutoHashMap = hash_map.AutoHashMap; pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; pub const BoundedArray = @import("bounded_array.zig").BoundedArray; +pub const BoundedArrayAligned = @import("bounded_array.zig").BoundedArrayAligned; pub const Build = @import("Build.zig"); pub const BufMap = @import("buf_map.zig").BufMap; pub const BufSet = @import("buf_set.zig").BufSet; From 2089b3f193beaef6debc5671ca6d1f6fc028db8d Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Fri, 17 Mar 2023 17:51:24 +0100 Subject: [PATCH 4/7] tls: use post-quantum secure key exchange (#14920) --- lib/std/crypto/tls.zig | 4 ++++ lib/std/crypto/tls/Client.zig | 37 ++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/std/crypto/tls.zig b/lib/std/crypto/tls.zig index 526a5f2175..5b5d3dea67 100644 --- a/lib/std/crypto/tls.zig +++ b/lib/std/crypto/tls.zig @@ -215,6 +215,10 @@ pub const NamedGroup = enum(u16) { ffdhe6144 = 0x0103, ffdhe8192 = 0x0104, + // Hybrid post-quantum key agreements + x25519_kyber512d00 = 0xFE30, + x25519_kyber768d00 = 0xFE31, + _, }; diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig index bc59459ff9..2b62d592ba 100644 --- a/lib/std/crypto/tls/Client.zig +++ b/lib/std/crypto/tls/Client.zig @@ -158,6 +158,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In // Only possible to happen if the private key is all zeroes. error.IdentityElement => return error.InsufficientEntropy, }; + const kyber768_kp = crypto.kem.kyber_d00.Kyber768.KeyPair.create(null) catch {}; const extensions_payload = tls.extension(.supported_versions, [_]u8{ @@ -175,6 +176,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In .rsa_pkcs1_sha512, .ed25519, })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{ + .x25519_kyber768d00, .secp256r1, .x25519, })) ++ tls.extension( @@ -182,7 +184,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In array(1, int2(@enumToInt(tls.NamedGroup.x25519)) ++ array(1, x25519_kp.public_key) ++ int2(@enumToInt(tls.NamedGroup.secp256r1)) ++ - array(1, secp256r1_kp.public_key.toUncompressedSec1())), + array(1, secp256r1_kp.public_key.toUncompressedSec1()) ++ + int2(@enumToInt(tls.NamedGroup.x25519_kyber768d00)) ++ + array(1, x25519_kp.public_key ++ kyber768_kp.public_key.toBytes())), ) ++ int2(@enumToInt(tls.ExtensionType.server_name)) ++ int2(host_len + 5) ++ // byte length of this extension payload @@ -274,7 +278,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In const extensions_size = hsd.decode(u16); var all_extd = try hsd.sub(extensions_size); var supported_version: u16 = 0; - var shared_key: [32]u8 = undefined; + var shared_key: []const u8 = undefined; var have_shared_key = false; while (!all_extd.eof()) { try all_extd.ensure(2 + 2); @@ -295,14 +299,29 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In const key_size = extd.decode(u16); try extd.ensure(key_size); switch (named_group) { - .x25519 => { - if (key_size != 32) return error.TlsIllegalParameter; - const server_pub_key = extd.array(32); + .x25519_kyber768d00 => { + const xksl = crypto.dh.X25519.public_length; + const hksl = xksl + crypto.kem.kyber_d00.Kyber768.ciphertext_length; + if (key_size != hksl) + return error.TlsIllegalParameter; + const server_ks = extd.array(hksl); - shared_key = crypto.dh.X25519.scalarmult( + shared_key = &((crypto.dh.X25519.scalarmult( + x25519_kp.secret_key, + server_ks[0..xksl].*, + ) catch return error.TlsDecryptFailure) ++ (kyber768_kp.secret_key.decaps( + server_ks[xksl..hksl], + ) catch return error.TlsDecryptFailure)); + }, + .x25519 => { + const ksl = crypto.dh.X25519.public_length; + if (key_size != ksl) return error.TlsIllegalParameter; + const server_pub_key = extd.array(ksl); + + shared_key = &(crypto.dh.X25519.scalarmult( x25519_kp.secret_key, server_pub_key.*, - ) catch return error.TlsDecryptFailure; + ) catch return error.TlsDecryptFailure); }, .secp256r1 => { const server_pub_key = extd.slice(key_size); @@ -314,7 +333,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In const mul = pk.p.mulPublic(secp256r1_kp.secret_key.bytes, .Big) catch { return error.TlsDecryptFailure; }; - shared_key = mul.affineCoordinates().x.toBytes(.Big); + shared_key = &mul.affineCoordinates().x.toBytes(.Big); }, else => { return error.TlsIllegalParameter; @@ -358,7 +377,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes); const empty_hash = tls.emptyHash(P.Hash); const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length); - p.handshake_secret = P.Hkdf.extract(&hs_derived_secret, &shared_key); + p.handshake_secret = P.Hkdf.extract(&hs_derived_secret, shared_key); const ap_derived_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "derived", &empty_hash, P.Hash.digest_length); p.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes); const client_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length); From 9f2aa3fbeec451021837ca6bca2baa47302f0468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 17 Mar 2023 15:09:26 +0200 Subject: [PATCH 5/7] Build.zig_exe: make it sentinel-aware This is useful for tests that want to `execve` zig directly. The string is already null-terminated, so this will just expose it as such, removing an extra allocation from the test. Will be used in #14462 --- lib/build_runner.zig | 4 ++-- lib/std/Build.zig | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 56dcf43155..930bccdc0f 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -1008,13 +1008,13 @@ fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype process.exit(1); } -fn nextArg(args: [][]const u8, idx: *usize) ?[]const u8 { +fn nextArg(args: [][:0]const u8, idx: *usize) ?[:0]const u8 { if (idx.* >= args.len) return null; defer idx.* += 1; return args[idx.*]; } -fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 { +fn argsRest(args: [][:0]const u8, idx: usize) ?[][:0]const u8 { if (idx >= args.len) return null; return args[idx..]; } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 632a45cf23..056a7ec639 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -60,7 +60,7 @@ verbose_cimport: bool, verbose_llvm_cpu_features: bool, reference_trace: ?u32 = null, invalid_user_input: bool, -zig_exe: []const u8, +zig_exe: [:0]const u8, default_step: *Step, env_map: *EnvMap, top_level_steps: std.StringArrayHashMapUnmanaged(*TopLevelStep), @@ -184,7 +184,7 @@ pub const DirList = struct { pub fn create( allocator: Allocator, - zig_exe: []const u8, + zig_exe: [:0]const u8, build_root: Cache.Directory, cache_root: Cache.Directory, global_cache_root: Cache.Directory, From 71e873703ffc49138d95cb66636856062341c420 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 17 Mar 2023 02:40:47 +0000 Subject: [PATCH 6/7] Sema: make @returnAddress return 0 at comptime See also #14938. Resolves: #14931 --- src/Sema.zig | 10 +++++++--- test/behavior.zig | 1 + test/behavior/return_address.zig | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 test/behavior/return_address.zig diff --git a/src/Sema.zig b/src/Sema.zig index cdbd8f8b41..0afce47e76 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -15454,9 +15454,13 @@ fn zirRetAddr( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); - try sema.requireRuntimeBlock(block, src, null); - return try block.addNoOp(.ret_addr); + _ = extended; + if (block.is_comptime) { + // TODO: we could give a meaningful lazy value here. #14938 + return sema.addIntUnsigned(Type.usize, 0); + } else { + return block.addNoOp(.ret_addr); + } } fn zirFrameAddress( diff --git a/test/behavior.zig b/test/behavior.zig index ed731377d8..b6fe8d120f 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -191,6 +191,7 @@ test { _ = @import("behavior/pub_enum.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("behavior/reflection.zig"); + _ = @import("behavior/return_address.zig"); _ = @import("behavior/saturating_arithmetic.zig"); _ = @import("behavior/select.zig"); _ = @import("behavior/shuffle.zig"); diff --git a/test/behavior/return_address.zig b/test/behavior/return_address.zig new file mode 100644 index 0000000000..fe48f21ec2 --- /dev/null +++ b/test/behavior/return_address.zig @@ -0,0 +1,17 @@ +const builtin = @import("builtin"); +const testing = @import("std").testing; + +fn retAddr() usize { + return @returnAddress(); +} + +test "return address" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + _ = retAddr(); + // TODO: #14938 + try testing.expectEqual(0, comptime retAddr()); +} From f56f3c5824af17516bcf3a0559b98cdad20bd416 Mon Sep 17 00:00:00 2001 From: hequn Date: Sat, 18 Mar 2023 03:58:02 +0800 Subject: [PATCH 7/7] Enable IPv4 mapped address conversion in linux version getAddressList (#14916) It seems like the original code of setsockopt is not effective because i catch the EINVAL branch when uncomment this code, it should call setsockopt before the bind call. This should fix issue #14900. Co-authored-by: Qun He --- lib/std/net.zig | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 7222433fd5..8800bbe6be 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -741,7 +741,7 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { return Stream{ .handle = sockfd }; } -const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || error{ +const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || std.os.SetSockOptError || error{ // TODO: break this up into error sets from the various underlying functions TemporaryNameServerFailure, @@ -1534,15 +1534,10 @@ fn resMSendRc( ns[i] = iplit.addr; assert(ns[i].getPort() == 53); if (iplit.addr.any.family != os.AF.INET) { - sl = @sizeOf(os.sockaddr.in6); family = os.AF.INET6; } } - // Get local address and open/bind a socket - var sa: Address = undefined; - @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(Address)); - sa.any.family = family; const flags = os.SOCK.DGRAM | os.SOCK.CLOEXEC | os.SOCK.NONBLOCK; const fd = os.socket(family, flags, 0) catch |err| switch (err) { error.AddressFamilyNotSupported => blk: { @@ -1556,27 +1551,35 @@ fn resMSendRc( else => |e| return e, }; defer os.closeSocket(fd); - try os.bind(fd, &sa.any, sl); // Past this point, there are no errors. Each individual query will // yield either no reply (indicated by zero length) or an answer // packet which is up to the caller to interpret. // Convert any IPv4 addresses in a mixed environment to v4-mapped - // TODO - //if (family == AF.INET6) { - // setsockopt(fd, IPPROTO.IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0); - // for (i=0; i