diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2023-11-10 22:27:45 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-10 22:27:45 -0500 |
| commit | 97e23896a9168132b6d36ca22ae1af10dd53d80d (patch) | |
| tree | b7d25c4231838edf980b7de7eec317f0a23371ee | |
| parent | 138a35df8f434115be04641b1df29514b0ef1cb8 (diff) | |
| parent | dfee782d7c30e2ff84060833be375e8cdf92e3be (diff) | |
| download | zig-97e23896a9168132b6d36ca22ae1af10dd53d80d.tar.gz zig-97e23896a9168132b6d36ca22ae1af10dd53d80d.zip | |
Merge pull request #17962 from ziglang/libssp
move libssp into libcompiler_rt
| -rw-r--r-- | lib/compiler_rt.zig | 1 | ||||
| -rw-r--r-- | lib/compiler_rt/ssp.zig | 143 | ||||
| -rw-r--r-- | lib/ssp.zig | 135 | ||||
| -rw-r--r-- | src/Compilation.zig | 97 | ||||
| -rw-r--r-- | src/link/Coff/lld.zig | 6 | ||||
| -rw-r--r-- | src/link/Elf.zig | 18 | ||||
| -rw-r--r-- | src/target.zig | 15 | ||||
| -rw-r--r-- | stage1/config.zig.in | 1 |
8 files changed, 181 insertions, 235 deletions
diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index f0503d2219..173e6af85a 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -233,5 +233,6 @@ comptime { _ = @import("compiler_rt/memmove.zig"); _ = @import("compiler_rt/memcmp.zig"); _ = @import("compiler_rt/bcmp.zig"); + _ = @import("compiler_rt/ssp.zig"); } } diff --git a/lib/compiler_rt/ssp.zig b/lib/compiler_rt/ssp.zig new file mode 100644 index 0000000000..236428e1ca --- /dev/null +++ b/lib/compiler_rt/ssp.zig @@ -0,0 +1,143 @@ +//! +//! Small Zig reimplementation of gcc's libssp. +//! +//! This library implements most of the builtins required by the stack smashing +//! protection as implemented by gcc&clang. +//! Missing exports: +//! - __gets_chk +//! - __mempcpy_chk +//! - __snprintf_chk +//! - __sprintf_chk +//! - __stpcpy_chk +//! - __vsnprintf_chk +//! - __vsprintf_chk + +const std = @import("std"); +const common = @import("./common.zig"); +const builtin = @import("builtin"); + +extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8; +extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8; +extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8; + +comptime { + @export(__stack_chk_fail, .{ .name = "__stack_chk_fail", .linkage = common.linkage, .visibility = common.visibility }); + @export(__chk_fail, .{ .name = "__chk_fail", .linkage = common.linkage, .visibility = common.visibility }); + @export(__stack_chk_guard, .{ .name = "__stack_chk_guard", .linkage = common.linkage, .visibility = common.visibility }); + @export(__strcpy_chk, .{ .name = "__strcpy_chk", .linkage = common.linkage, .visibility = common.visibility }); + @export(__strncpy_chk, .{ .name = "__strncpy_chk", .linkage = common.linkage, .visibility = common.visibility }); + @export(__strcat_chk, .{ .name = "__strcat_chk", .linkage = common.linkage, .visibility = common.visibility }); + @export(__strncat_chk, .{ .name = "__strncat_chk", .linkage = common.linkage, .visibility = common.visibility }); + @export(__memcpy_chk, .{ .name = "__memcpy_chk", .linkage = common.linkage, .visibility = common.visibility }); + @export(__memmove_chk, .{ .name = "__memmove_chk", .linkage = common.linkage, .visibility = common.visibility }); + @export(__memset_chk, .{ .name = "__memset_chk", .linkage = common.linkage, .visibility = common.visibility }); +} + +fn __stack_chk_fail() callconv(.C) noreturn { + @panic("stack smashing detected"); +} + +fn __chk_fail() callconv(.C) noreturn { + @panic("buffer overflow detected"); +} + +// TODO: Initialize the canary with random data +var __stack_chk_guard: usize = blk: { + var buf = [1]u8{0} ** @sizeOf(usize); + buf[@sizeOf(usize) - 1] = 255; + buf[@sizeOf(usize) - 2] = '\n'; + break :blk @as(usize, @bitCast(buf)); +}; + +fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 { + @setRuntimeSafety(false); + + var i: usize = 0; + while (i < dest_n and src[i] != 0) : (i += 1) { + dest[i] = src[i]; + } + + if (i == dest_n) __chk_fail(); + + dest[i] = 0; + + return dest; +} + +fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 { + @setRuntimeSafety(false); + if (dest_n < n) __chk_fail(); + var i: usize = 0; + while (i < n and src[i] != 0) : (i += 1) { + dest[i] = src[i]; + } + while (i < n) : (i += 1) { + dest[i] = 0; + } + return dest; +} + +fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 { + @setRuntimeSafety(false); + + var avail = dest_n; + + var dest_end: usize = 0; + while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) { + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + var i: usize = 0; + while (avail > 0 and src[i] != 0) : (i += 1) { + dest[dest_end + i] = src[i]; + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + dest[dest_end + i] = 0; + + return dest; +} + +fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 { + @setRuntimeSafety(false); + + var avail = dest_n; + + var dest_end: usize = 0; + while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) { + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + var i: usize = 0; + while (avail > 0 and i < n and src[i] != 0) : (i += 1) { + dest[dest_end + i] = src[i]; + avail -= 1; + } + + if (avail < 1) __chk_fail(); + + dest[dest_end + i] = 0; + + return dest; +} + +fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) __chk_fail(); + return memcpy(dest, src, n); +} + +fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) __chk_fail(); + return memmove(dest, src, n); +} + +fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) __chk_fail(); + return memset(dest, c, n); +} diff --git a/lib/ssp.zig b/lib/ssp.zig deleted file mode 100644 index 4f8eba567f..0000000000 --- a/lib/ssp.zig +++ /dev/null @@ -1,135 +0,0 @@ -//! -//! Small Zig reimplementation of gcc's libssp. -//! -//! This library implements most of the builtins required by the stack smashing -//! protection as implemented by gcc&clang. -//! Missing exports: -//! - __gets_chk -//! - __mempcpy_chk -//! - __snprintf_chk -//! - __sprintf_chk -//! - __stpcpy_chk -//! - __vsnprintf_chk -//! - __vsprintf_chk - -const std = @import("std"); - -extern fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8; -extern fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8; -extern fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8; -extern fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8; - -// Avoid dragging in the runtime safety mechanisms into this .o file. -pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = msg; - _ = error_return_trace; - @setCold(true); - std.os.abort(); -} - -export fn __stack_chk_fail() callconv(.C) noreturn { - @panic("stack smashing detected"); -} - -export fn __chk_fail() callconv(.C) noreturn { - @panic("buffer overflow detected"); -} - -// Emitted when targeting some architectures (eg. x86) -// XXX: This symbol should be hidden -export fn __stack_chk_fail_local() callconv(.C) noreturn { - __stack_chk_fail(); -} - -// XXX: Initialize the canary with random data -export var __stack_chk_guard: usize = blk: { - var buf = [1]u8{0} ** @sizeOf(usize); - buf[@sizeOf(usize) - 1] = 255; - buf[@sizeOf(usize) - 2] = '\n'; - break :blk @as(usize, @bitCast(buf)); -}; - -export fn __strcpy_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 { - @setRuntimeSafety(false); - - var i: usize = 0; - while (i < dest_n and src[i] != 0) : (i += 1) { - dest[i] = src[i]; - } - - if (i == dest_n) __chk_fail(); - - dest[i] = 0; - - return dest; -} - -export fn __strncpy_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 { - if (dest_n < n) __chk_fail(); - return strncpy(dest, src, n); -} - -export fn __strcat_chk(dest: [*:0]u8, src: [*:0]const u8, dest_n: usize) callconv(.C) [*:0]u8 { - @setRuntimeSafety(false); - - var avail = dest_n; - - var dest_end: usize = 0; - while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) { - avail -= 1; - } - - if (avail < 1) __chk_fail(); - - var i: usize = 0; - while (avail > 0 and src[i] != 0) : (i += 1) { - dest[dest_end + i] = src[i]; - avail -= 1; - } - - if (avail < 1) __chk_fail(); - - dest[dest_end + i] = 0; - - return dest; -} - -export fn __strncat_chk(dest: [*:0]u8, src: [*:0]const u8, n: usize, dest_n: usize) callconv(.C) [*:0]u8 { - @setRuntimeSafety(false); - - var avail = dest_n; - - var dest_end: usize = 0; - while (avail > 0 and dest[dest_end] != 0) : (dest_end += 1) { - avail -= 1; - } - - if (avail < 1) __chk_fail(); - - var i: usize = 0; - while (avail > 0 and i < n and src[i] != 0) : (i += 1) { - dest[dest_end + i] = src[i]; - avail -= 1; - } - - if (avail < 1) __chk_fail(); - - dest[dest_end + i] = 0; - - return dest; -} - -export fn __memcpy_chk(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { - if (dest_n < n) __chk_fail(); - return memcpy(dest, src, n); -} - -export fn __memmove_chk(dest: ?[*]u8, src: ?[*]const u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { - if (dest_n < n) __chk_fail(); - return memmove(dest, src, n); -} - -export fn __memset_chk(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { - if (dest_n < n) __chk_fail(); - return memset(dest, c, n); -} diff --git a/src/Compilation.zig b/src/Compilation.zig index 3bd7d1506c..00b7c50357 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -152,9 +152,6 @@ libunwind_static_lib: ?CRTFile = null, /// Populated when we build the TSAN static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). tsan_static_lib: ?CRTFile = null, -/// Populated when we build the libssp static library. A Job to build this is placed in the queue -/// and resolved before calling linker.flush(). -libssp_static_lib: ?CRTFile = null, /// Populated when we build the libc static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). libc_static_lib: ?CRTFile = null, @@ -286,7 +283,6 @@ const Job = union(enum) { libcxx: void, libcxxabi: void, libtsan: void, - libssp: void, /// needed when not linking libc and using LLVM for code generation because it generates /// calls to, for example, memcpy and memset. zig_libc: void, @@ -683,7 +679,6 @@ pub const MiscTask = enum { libtsan, wasi_libc_crt_file, compiler_rt, - libssp, zig_libc, analyze_mod, @@ -1072,8 +1067,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .Exe => true, }; - const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib; - // WASI-only. Resolve the optional exec-model option, defaults to command. const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command; @@ -1355,10 +1348,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (stack_check and !target_util.supportsStackProbing(options.target)) return error.StackCheckUnsupportedByTarget; - const capable_of_building_ssp = canBuildLibSsp(options.target, use_llvm); - - const stack_protector: u32 = options.want_stack_protector orelse b: { - if (!target_util.supportsStackProtector(options.target)) break :b @as(u32, 0); + const stack_protector: u32 = sp: { + const zig_backend = zigBackend(options.target, use_llvm); + if (!target_util.supportsStackProtector(options.target, zig_backend)) { + if (options.want_stack_protector) |x| { + if (x > 0) return error.StackProtectorUnsupportedByTarget; + } + break :sp 0; + } // This logic is checking for linking libc because otherwise our start code // which is trying to set up TLS (i.e. the fs/gs registers) but the stack @@ -1367,21 +1364,20 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // as being exempt from stack protection checks, we could change this logic // to supporting stack protection even when not linking libc. // TODO file issue about this - if (!link_libc) break :b 0; - if (!capable_of_building_ssp) break :b 0; - if (is_safe_mode) break :b default_stack_protector_buffer_size; - break :b 0; + if (!link_libc) { + if (options.want_stack_protector) |x| { + if (x > 0) return error.StackProtectorUnavailableWithoutLibC; + } + break :sp 0; + } + + if (options.want_stack_protector) |x| break :sp x; + if (is_safe_mode) break :sp default_stack_protector_buffer_size; + break :sp 0; }; - if (stack_protector != 0) { - if (!target_util.supportsStackProtector(options.target)) - return error.StackProtectorUnsupportedByTarget; - if (!capable_of_building_ssp) - return error.StackProtectorUnsupportedByBackend; - if (!link_libc) - return error.StackProtectorUnavailableWithoutLibC; - } - const include_compiler_rt = options.want_compiler_rt orelse needs_c_symbols; + const include_compiler_rt = options.want_compiler_rt orelse + (!options.skip_linker_dependencies and is_exe_or_dyn_lib); const single_threaded = st: { if (target_util.isSingleThreaded(options.target)) { @@ -2196,18 +2192,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { comp.job_queued_compiler_rt_obj = true; } } - if (needs_c_symbols) { - // Related: https://github.com/ziglang/zig/issues/7265. - if (comp.bin_file.options.stack_protector != 0 and - (!comp.bin_file.options.link_libc or - !target_util.libcProvidesStackProtector(target))) - { - try comp.work_queue.writeItem(.{ .libssp = {} }); - } - if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) { - try comp.work_queue.writeItem(.{ .zig_libc = {} }); - } + if (!comp.bin_file.options.skip_linker_dependencies and is_exe_or_dyn_lib and + !comp.bin_file.options.link_libc and capable_of_building_zig_libc) + { + try comp.work_queue.writeItem(.{ .zig_libc = {} }); } } @@ -2253,9 +2242,6 @@ pub fn destroy(self: *Compilation) void { if (self.compiler_rt_obj) |*crt_file| { crt_file.deinit(gpa); } - if (self.libssp_static_lib) |*crt_file| { - crt_file.deinit(gpa); - } if (self.libc_static_lib) |*crt_file| { crt_file.deinit(gpa); } @@ -4022,26 +4008,6 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v ); }; }, - .libssp => { - const named_frame = tracy.namedFrame("libssp"); - defer named_frame.end(); - - comp.buildOutputFromZig( - "ssp.zig", - .Lib, - &comp.libssp_static_lib, - .libssp, - prog_node, - ) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.SubCompilationFailed => return, // error reported already - else => comp.lockAndSetMiscFailure( - .libssp, - "unable to build libssp: {s}", - .{@errorName(err)}, - ), - }; - }, .zig_libc => { const named_frame = tracy.namedFrame("zig_libc"); defer named_frame.end(); @@ -6526,21 +6492,6 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool { }; } -fn canBuildLibSsp(target: std.Target, use_llvm: bool) bool { - switch (target.os.tag) { - .plan9 => return false, - else => {}, - } - switch (target.cpu.arch) { - .spirv32, .spirv64 => return false, - else => {}, - } - return switch (zigBackend(target, use_llvm)) { - .stage2_llvm => true, - else => build_options.have_llvm, - }; -} - /// Not to be confused with canBuildLibC, which builds musl, glibc, and similar. /// This one builds lib/c.zig. fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index cbefcbd91e..573a85e167 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -483,12 +483,6 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(lib.full_object_path); } } - // MinGW doesn't provide libssp symbols - if (target.abi.isGnu()) { - if (comp.libssp_static_lib) |lib| { - try argv.append(lib.full_object_path); - } - } // MSVC compiler_rt is missing some stuff, so we build it unconditionally but // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d210292bf9..b21a7254ff 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1040,12 +1040,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } - // stack-protector. - // Related: https://github.com/ziglang/zig/issues/7265 - if (comp.libssp_static_lib) |ssp| { - try positionals.append(.{ .path = ssp.full_object_path }); - } - for (positionals.items) |obj| { var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| @@ -1689,12 +1683,6 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } } - // stack-protector. - // Related: https://github.com/ziglang/zig/issues/7265 - if (comp.libssp_static_lib) |ssp| { - try argv.append(ssp.full_object_path); - } - // Shared libraries. // Worst-case, we need an --as-needed argument for every lib, as well // as one before and one after. @@ -2729,12 +2717,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - // stack-protector. - // Related: https://github.com/ziglang/zig/issues/7265 - if (comp.libssp_static_lib) |ssp| { - try argv.append(ssp.full_object_path); - } - // Shared libraries. if (is_exe_or_dyn_lib) { const system_libs = self.base.options.system_libs.keys(); diff --git a/src/target.zig b/src/target.zig index 7f85113040..59528ed139 100644 --- a/src/target.zig +++ b/src/target.zig @@ -328,8 +328,19 @@ pub fn supportsStackProbing(target: std.Target) bool { (target.cpu.arch == .x86 or target.cpu.arch == .x86_64); } -pub fn supportsStackProtector(target: std.Target) bool { - return !target.isSpirV(); +pub fn supportsStackProtector(target: std.Target, backend: std.builtin.CompilerBackend) bool { + switch (target.os.tag) { + .plan9 => return false, + else => {}, + } + switch (target.cpu.arch) { + .spirv32, .spirv64 => return false, + else => {}, + } + return switch (backend) { + .stage2_llvm => true, + else => false, + }; } pub fn libcProvidesStackProtector(target: std.Target) bool { diff --git a/stage1/config.zig.in b/stage1/config.zig.in index 4db7babbd5..96215bb362 100644 --- a/stage1/config.zig.in +++ b/stage1/config.zig.in @@ -9,7 +9,6 @@ pub const enable_logging: bool = false; pub const enable_link_snapshots: bool = false; pub const enable_tracy = false; pub const value_tracing = false; -pub const have_stage1 = false; pub const skip_non_native = false; pub const only_c = false; pub const force_gpa = false; |
