authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-19 06:41:34-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-08-19 06:41:34-04:00
logbe2bd5848a880765f4bc7e2363ef201a0930a04b
tree868694d734e82cb93b52b6c0c155d932f5f65f8f
parent2ccaa5414b904edb2f4af293291f5401d106f277
parentfdb934a157230ae6d3f11dee8d0d62013d5b919e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12472 from ziglang/stage2-stack-protector

stage2: implement stack protectors

15 files changed, 193 insertions(+), 61 deletions(-)

src/Compilation.zig+76-45
...@@ -173,6 +173,7 @@ astgen_wait_group: WaitGroup = .{},...@@ -173,6 +173,7 @@ astgen_wait_group: WaitGroup = .{},
173/// TODO: Remove this when Stage2 becomes the default compiler as it will already have this information.173/// TODO: Remove this when Stage2 becomes the default compiler as it will already have this information.
174export_symbol_names: std.ArrayListUnmanaged([]const u8) = .{},174export_symbol_names: std.ArrayListUnmanaged([]const u8) = .{},
175175
176pub const default_stack_protector_buffer_size = 4;
176pub const SemaError = Module.SemaError;177pub const SemaError = Module.SemaError;
177178
178pub const CRTFile = struct {179pub const CRTFile = struct {
...@@ -837,6 +838,10 @@ pub const InitOptions = struct {...@@ -837,6 +838,10 @@ pub const InitOptions = struct {
837 want_pie: ?bool = null,838 want_pie: ?bool = null,
838 want_sanitize_c: ?bool = null,839 want_sanitize_c: ?bool = null,
839 want_stack_check: ?bool = null,840 want_stack_check: ?bool = null,
841 /// null means default.
842 /// 0 means no stack protector.
843 /// other number means stack protection with that buffer size.
844 want_stack_protector: ?u32 = null,
840 want_red_zone: ?bool = null,845 want_red_zone: ?bool = null,
841 omit_frame_pointer: ?bool = null,846 omit_frame_pointer: ?bool = null,
842 want_valgrind: ?bool = null,847 want_valgrind: ?bool = null,
...@@ -1014,6 +1019,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1014,6 +1019,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1014 return error.ExportTableAndImportTableConflict;1019 return error.ExportTableAndImportTableConflict;
1015 }1020 }
10161021
1022 // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt.
1023 // Once they are capable this condition could be removed. When removing this condition,
1024 // also test the use case of `build-obj -fcompiler-rt` with the native backends
1025 // and make sure the compiler-rt symbols are emitted.
1026 const capable_of_building_compiler_rt = build_options.have_llvm;
1027
1028 const capable_of_building_zig_libc = build_options.have_llvm;
1029 const capable_of_building_ssp = build_options.have_llvm;
1030
1017 const comp: *Compilation = comp: {1031 const comp: *Compilation = comp: {
1018 // For allocations that have the same lifetime as Compilation. This arena is used only during this1032 // For allocations that have the same lifetime as Compilation. This arena is used only during this
1019 // initialization and then is freed in deinit().1033 // initialization and then is freed in deinit().
...@@ -1289,11 +1303,36 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1289,11 +1303,36 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12891303
1290 const sanitize_c = options.want_sanitize_c orelse is_safe_mode;1304 const sanitize_c = options.want_sanitize_c orelse is_safe_mode;
12911305
1292 const stack_check: bool = b: {1306 const stack_check: bool = options.want_stack_check orelse b: {
1293 if (!target_util.supportsStackProbing(options.target))1307 if (!target_util.supportsStackProbing(options.target)) break :b false;
1294 break :b false;1308 break :b is_safe_mode;
1295 break :b options.want_stack_check orelse is_safe_mode;
1296 };1309 };
1310 if (stack_check and !target_util.supportsStackProbing(options.target))
1311 return error.StackCheckUnsupportedByTarget;
1312
1313 const stack_protector: u32 = options.want_stack_protector orelse b: {
1314 if (!target_util.supportsStackProtector(options.target)) break :b @as(u32, 0);
1315
1316 // This logic is checking for linking libc because otherwise our start code
1317 // which is trying to set up TLS (i.e. the fs/gs registers) but the stack
1318 // protection code depends on fs/gs registers being already set up.
1319 // If we were able to annotate start code, or perhaps the entire std lib,
1320 // as being exempt from stack protection checks, we could change this logic
1321 // to supporting stack protection even when not linking libc.
1322 // TODO file issue about this
1323 if (!link_libc) break :b 0;
1324 if (!capable_of_building_ssp) break :b 0;
1325 if (is_safe_mode) break :b default_stack_protector_buffer_size;
1326 break :b 0;
1327 };
1328 if (stack_protector != 0) {
1329 if (!target_util.supportsStackProtector(options.target))
1330 return error.StackProtectorUnsupportedByTarget;
1331 if (!capable_of_building_ssp)
1332 return error.StackProtectorUnsupportedByBackend;
1333 if (!link_libc)
1334 return error.StackProtectorUnavailableWithoutLibC;
1335 }
12971336
1298 const valgrind: bool = b: {1337 const valgrind: bool = b: {
1299 if (!target_util.hasValgrindSupport(options.target))1338 if (!target_util.hasValgrindSupport(options.target))
...@@ -1378,6 +1417,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1378,6 +1417,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1378 cache.hash.add(unwind_tables);1417 cache.hash.add(unwind_tables);
1379 cache.hash.add(tsan);1418 cache.hash.add(tsan);
1380 cache.hash.add(stack_check);1419 cache.hash.add(stack_check);
1420 cache.hash.add(stack_protector);
1381 cache.hash.add(red_zone);1421 cache.hash.add(red_zone);
1382 cache.hash.add(omit_frame_pointer);1422 cache.hash.add(omit_frame_pointer);
1383 cache.hash.add(link_mode);1423 cache.hash.add(link_mode);
...@@ -1741,6 +1781,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1741,6 +1781,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1741 .valgrind = valgrind,1781 .valgrind = valgrind,
1742 .tsan = tsan,1782 .tsan = tsan,
1743 .stack_check = stack_check,1783 .stack_check = stack_check,
1784 .stack_protector = stack_protector,
1744 .red_zone = red_zone,1785 .red_zone = red_zone,
1745 .omit_frame_pointer = omit_frame_pointer,1786 .omit_frame_pointer = omit_frame_pointer,
1746 .single_threaded = single_threaded,1787 .single_threaded = single_threaded,
...@@ -1822,6 +1863,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1822,6 +1863,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1822 };1863 };
1823 errdefer comp.destroy();1864 errdefer comp.destroy();
18241865
1866 const target = comp.getTarget();
1867
1825 // Add a `CObject` for each `c_source_files`.1868 // Add a `CObject` for each `c_source_files`.
1826 try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);1869 try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);
1827 for (options.c_source_files) |c_source_file| {1870 for (options.c_source_files) |c_source_file| {
...@@ -1837,11 +1880,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1837,11 +1880,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18371880
1838 const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null;1881 const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null;
18391882
1840 if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies and1883 if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies and target.ofmt != .c) {
1841 options.target.ofmt != .c)1884 if (target.isDarwin()) {
1842 {1885 switch (target.abi) {
1843 if (comp.getTarget().isDarwin()) {
1844 switch (comp.getTarget().abi) {
1845 .none,1886 .none,
1846 .simulator,1887 .simulator,
1847 .macabi,1888 .macabi,
...@@ -1852,9 +1893,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1852,9 +1893,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1852 // If we need to build glibc for the target, add work items for it.1893 // If we need to build glibc for the target, add work items for it.
1853 // We go through the work queue so that building can be done in parallel.1894 // We go through the work queue so that building can be done in parallel.
1854 if (comp.wantBuildGLibCFromSource()) {1895 if (comp.wantBuildGLibCFromSource()) {
1855 if (!target_util.canBuildLibC(comp.getTarget())) return error.LibCUnavailable;1896 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
18561897
1857 if (glibc.needsCrtiCrtn(comp.getTarget())) {1898 if (glibc.needsCrtiCrtn(target)) {
1858 try comp.work_queue.write(&[_]Job{1899 try comp.work_queue.write(&[_]Job{
1859 .{ .glibc_crt_file = .crti_o },1900 .{ .glibc_crt_file = .crti_o },
1860 .{ .glibc_crt_file = .crtn_o },1901 .{ .glibc_crt_file = .crtn_o },
...@@ -1867,10 +1908,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1867,10 +1908,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1867 });1908 });
1868 }1909 }
1869 if (comp.wantBuildMuslFromSource()) {1910 if (comp.wantBuildMuslFromSource()) {
1870 if (!target_util.canBuildLibC(comp.getTarget())) return error.LibCUnavailable;1911 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
18711912
1872 try comp.work_queue.ensureUnusedCapacity(6);1913 try comp.work_queue.ensureUnusedCapacity(6);
1873 if (musl.needsCrtiCrtn(comp.getTarget())) {1914 if (musl.needsCrtiCrtn(target)) {
1874 comp.work_queue.writeAssumeCapacity(&[_]Job{1915 comp.work_queue.writeAssumeCapacity(&[_]Job{
1875 .{ .musl_crt_file = .crti_o },1916 .{ .musl_crt_file = .crti_o },
1876 .{ .musl_crt_file = .crtn_o },1917 .{ .musl_crt_file = .crtn_o },
...@@ -1887,7 +1928,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1887,7 +1928,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1887 });1928 });
1888 }1929 }
1889 if (comp.wantBuildWasiLibcFromSource()) {1930 if (comp.wantBuildWasiLibcFromSource()) {
1890 if (!target_util.canBuildLibC(comp.getTarget())) return error.LibCUnavailable;1931 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
18911932
1892 const wasi_emulated_libs = comp.bin_file.options.wasi_emulated_libs;1933 const wasi_emulated_libs = comp.bin_file.options.wasi_emulated_libs;
1893 try comp.work_queue.ensureUnusedCapacity(wasi_emulated_libs.len + 2); // worst-case we need all components1934 try comp.work_queue.ensureUnusedCapacity(wasi_emulated_libs.len + 2); // worst-case we need all components
...@@ -1902,7 +1943,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1902,7 +1943,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1902 });1943 });
1903 }1944 }
1904 if (comp.wantBuildMinGWFromSource()) {1945 if (comp.wantBuildMinGWFromSource()) {
1905 if (!target_util.canBuildLibC(comp.getTarget())) return error.LibCUnavailable;1946 if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
19061947
1907 const static_lib_jobs = [_]Job{1948 const static_lib_jobs = [_]Job{
1908 .{ .mingw_crt_file = .mingw32_lib },1949 .{ .mingw_crt_file = .mingw32_lib },
...@@ -1921,7 +1962,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1921,7 +1962,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1921 }1962 }
1922 }1963 }
1923 // Generate Windows import libs.1964 // Generate Windows import libs.
1924 if (comp.getTarget().os.tag == .windows) {1965 if (target.os.tag == .windows) {
1925 const count = comp.bin_file.options.system_libs.count();1966 const count = comp.bin_file.options.system_libs.count();
1926 try comp.work_queue.ensureUnusedCapacity(count);1967 try comp.work_queue.ensureUnusedCapacity(count);
1927 var i: usize = 0;1968 var i: usize = 0;
...@@ -1940,15 +1981,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1940,15 +1981,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1940 try comp.work_queue.writeItem(.libtsan);1981 try comp.work_queue.writeItem(.libtsan);
1941 }1982 }
19421983
1943 // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt.
1944 // Once they are capable this condition could be removed. When removing this condition,
1945 // also test the use case of `build-obj -fcompiler-rt` with the native backends
1946 // and make sure the compiler-rt symbols are emitted.
1947 const capable_of_building_compiler_rt = build_options.have_llvm;
1948
1949 const capable_of_building_zig_libc = build_options.have_llvm;
1950 const capable_of_building_ssp = comp.bin_file.options.use_stage1;
1951
1952 if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) {1984 if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) {
1953 if (is_exe_or_dyn_lib) {1985 if (is_exe_or_dyn_lib) {
1954 log.debug("queuing a job to build compiler_rt_lib", .{});1986 log.debug("queuing a job to build compiler_rt_lib", .{});
...@@ -1962,8 +1994,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1962,8 +1994,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1962 }1994 }
1963 }1995 }
1964 if (needs_c_symbols) {1996 if (needs_c_symbols) {
1965 // MinGW provides no libssp, use our own implementation.1997 // Related: https://github.com/ziglang/zig/issues/7265.
1966 if (comp.getTarget().isMinGW() and capable_of_building_ssp) {1998 if (comp.bin_file.options.stack_protector != 0 and
1999 (!comp.bin_file.options.link_libc or
2000 !target_util.libcProvidesStackProtector(target)))
2001 {
1967 try comp.work_queue.writeItem(.{ .libssp = {} });2002 try comp.work_queue.writeItem(.{ .libssp = {} });
1968 }2003 }
19692004
...@@ -4123,6 +4158,17 @@ pub fn addCCArgs(...@@ -4123,6 +4158,17 @@ pub fn addCCArgs(
4123 try argv.append("-fno-omit-frame-pointer");4158 try argv.append("-fno-omit-frame-pointer");
4124 }4159 }
41254160
4161 const ssp_buf_size = comp.bin_file.options.stack_protector;
4162 if (ssp_buf_size != 0) {
4163 try argv.appendSlice(&[_][]const u8{
4164 "-fstack-protector-strong",
4165 "--param",
4166 try std.fmt.allocPrint(arena, "ssp-buffer-size={d}", .{ssp_buf_size}),
4167 });
4168 } else {
4169 try argv.append("-fno-stack-protector");
4170 }
4171
4126 switch (comp.bin_file.options.optimize_mode) {4172 switch (comp.bin_file.options.optimize_mode) {
4127 .Debug => {4173 .Debug => {
4128 // windows c runtime requires -D_DEBUG if using debug libraries4174 // windows c runtime requires -D_DEBUG if using debug libraries
...@@ -4131,27 +4177,12 @@ pub fn addCCArgs(...@@ -4131,27 +4177,12 @@ pub fn addCCArgs(
4131 // to -O1. Besides potentially impairing debugging, -O1/-Og significantly4177 // to -O1. Besides potentially impairing debugging, -O1/-Og significantly
4132 // increases compile times.4178 // increases compile times.
4133 try argv.append("-O0");4179 try argv.append("-O0");
4134
4135 if (comp.bin_file.options.link_libc and target.os.tag != .wasi) {
4136 try argv.append("-fstack-protector-strong");
4137 try argv.append("--param");
4138 try argv.append("ssp-buffer-size=4");
4139 } else {
4140 try argv.append("-fno-stack-protector");
4141 }
4142 },4180 },
4143 .ReleaseSafe => {4181 .ReleaseSafe => {
4144 // See the comment in the BuildModeFastRelease case for why we pass -O2 rather4182 // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
4145 // than -O3 here.4183 // than -O3 here.
4146 try argv.append("-O2");4184 try argv.append("-O2");
4147 if (comp.bin_file.options.link_libc and target.os.tag != .wasi) {4185 try argv.append("-D_FORTIFY_SOURCE=2");
4148 try argv.append("-D_FORTIFY_SOURCE=2");
4149 try argv.append("-fstack-protector-strong");
4150 try argv.append("--param");
4151 try argv.append("ssp-buffer-size=4");
4152 } else {
4153 try argv.append("-fno-stack-protector");
4154 }
4155 },4186 },
4156 .ReleaseFast => {4187 .ReleaseFast => {
4157 try argv.append("-DNDEBUG");4188 try argv.append("-DNDEBUG");
...@@ -4161,12 +4192,10 @@ pub fn addCCArgs(...@@ -4161,12 +4192,10 @@ pub fn addCCArgs(
4161 // Zig code than it is for C code. Also, C programmers are used to their code4192 // Zig code than it is for C code. Also, C programmers are used to their code
4162 // running in -O2 and thus the -O3 path has been tested less.4193 // running in -O2 and thus the -O3 path has been tested less.
4163 try argv.append("-O2");4194 try argv.append("-O2");
4164 try argv.append("-fno-stack-protector");
4165 },4195 },
4166 .ReleaseSmall => {4196 .ReleaseSmall => {
4167 try argv.append("-DNDEBUG");4197 try argv.append("-DNDEBUG");
4168 try argv.append("-Os");4198 try argv.append("-Os");
4169 try argv.append("-fno-stack-protector");
4170 },4199 },
4171 }4200 }
41724201
...@@ -5031,6 +5060,7 @@ fn buildOutputFromZig(...@@ -5031,6 +5060,7 @@ fn buildOutputFromZig(
5031 .use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1,5060 .use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1,
5032 .want_sanitize_c = false,5061 .want_sanitize_c = false,
5033 .want_stack_check = false,5062 .want_stack_check = false,
5063 .want_stack_protector = 0,
5034 .want_red_zone = comp.bin_file.options.red_zone,5064 .want_red_zone = comp.bin_file.options.red_zone,
5035 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,5065 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
5036 .want_valgrind = false,5066 .want_valgrind = false,
...@@ -5311,6 +5341,7 @@ pub fn build_crt_file(...@@ -5311,6 +5341,7 @@ pub fn build_crt_file(
5311 .optimize_mode = comp.compilerRtOptMode(),5341 .optimize_mode = comp.compilerRtOptMode(),
5312 .want_sanitize_c = false,5342 .want_sanitize_c = false,
5313 .want_stack_check = false,5343 .want_stack_check = false,
5344 .want_stack_protector = 0,
5314 .want_red_zone = comp.bin_file.options.red_zone,5345 .want_red_zone = comp.bin_file.options.red_zone,
5315 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,5346 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
5316 .want_valgrind = false,5347 .want_valgrind = false,
src/Sema.zig+8-7
...@@ -7567,10 +7567,11 @@ fn handleExternLibName(...@@ -7567,10 +7567,11 @@ fn handleExternLibName(
7567) CompileError![:0]u8 {7567) CompileError![:0]u8 {
7568 blk: {7568 blk: {
7569 const mod = sema.mod;7569 const mod = sema.mod;
7570 const comp = mod.comp;
7570 const target = mod.getTarget();7571 const target = mod.getTarget();
7571 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});7572 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
7572 if (target_util.is_libc_lib_name(target, lib_name)) {7573 if (target_util.is_libc_lib_name(target, lib_name)) {
7573 if (!mod.comp.bin_file.options.link_libc) {7574 if (!comp.bin_file.options.link_libc and !comp.bin_file.options.parent_compilation_link_libc) {
7574 return sema.fail(7575 return sema.fail(
7575 block,7576 block,
7576 src_loc,7577 src_loc,
...@@ -7578,11 +7579,11 @@ fn handleExternLibName(...@@ -7578,11 +7579,11 @@ fn handleExternLibName(
7578 .{},7579 .{},
7579 );7580 );
7580 }7581 }
7581 mod.comp.bin_file.options.link_libc = true;7582 comp.bin_file.options.link_libc = true;
7582 break :blk;7583 break :blk;
7583 }7584 }
7584 if (target_util.is_libcpp_lib_name(target, lib_name)) {7585 if (target_util.is_libcpp_lib_name(target, lib_name)) {
7585 if (!mod.comp.bin_file.options.link_libcpp) {7586 if (!comp.bin_file.options.link_libcpp) {
7586 return sema.fail(7587 return sema.fail(
7587 block,7588 block,
7588 src_loc,7589 src_loc,
...@@ -7590,14 +7591,14 @@ fn handleExternLibName(...@@ -7590,14 +7591,14 @@ fn handleExternLibName(
7590 .{},7591 .{},
7591 );7592 );
7592 }7593 }
7593 mod.comp.bin_file.options.link_libcpp = true;7594 comp.bin_file.options.link_libcpp = true;
7594 break :blk;7595 break :blk;
7595 }7596 }
7596 if (mem.eql(u8, lib_name, "unwind")) {7597 if (mem.eql(u8, lib_name, "unwind")) {
7597 mod.comp.bin_file.options.link_libunwind = true;7598 comp.bin_file.options.link_libunwind = true;
7598 break :blk;7599 break :blk;
7599 }7600 }
7600 if (!target.isWasm() and !mod.comp.bin_file.options.pic) {7601 if (!target.isWasm() and !comp.bin_file.options.pic) {
7601 return sema.fail(7602 return sema.fail(
7602 block,7603 block,
7603 src_loc,7604 src_loc,
...@@ -7605,7 +7606,7 @@ fn handleExternLibName(...@@ -7605,7 +7606,7 @@ fn handleExternLibName(
7605 .{ lib_name, lib_name },7606 .{ lib_name, lib_name },
7606 );7607 );
7607 }7608 }
7608 mod.comp.stage1AddLinkLib(lib_name) catch |err| {7609 comp.stage1AddLinkLib(lib_name) catch |err| {
7609 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{7610 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
7610 lib_name, @errorName(err),7611 lib_name, @errorName(err),
7611 });7612 });
src/clang_options_data.zig+40-5
...@@ -3290,7 +3290,14 @@ flagpd1("fno-stack-arrays"),...@@ -3290,7 +3290,14 @@ flagpd1("fno-stack-arrays"),
3290 .psl = false,3290 .psl = false,
3291},3291},
3292flagpd1("fno-stack-clash-protection"),3292flagpd1("fno-stack-clash-protection"),
3293flagpd1("fno-stack-protector"),3293.{
3294 .name = "fno-stack-protector",
3295 .syntax = .flag,
3296 .zig_equivalent = .no_stack_protector,
3297 .pd1 = true,
3298 .pd2 = false,
3299 .psl = false,
3300},
3294flagpd1("fno-stack-size-section"),3301flagpd1("fno-stack-size-section"),
3295flagpd1("fno-standalone-debug"),3302flagpd1("fno-standalone-debug"),
3296flagpd1("fno-strength-reduce"),3303flagpd1("fno-strength-reduce"),
...@@ -3588,9 +3595,30 @@ flagpd1("fstack-arrays"),...@@ -3588,9 +3595,30 @@ flagpd1("fstack-arrays"),
3588 .psl = false,3595 .psl = false,
3589},3596},
3590flagpd1("fstack-clash-protection"),3597flagpd1("fstack-clash-protection"),
3591flagpd1("fstack-protector"),3598.{
3592flagpd1("fstack-protector-all"),3599 .name = "fstack-protector",
3593flagpd1("fstack-protector-strong"),3600 .syntax = .flag,
3601 .zig_equivalent = .stack_protector,
3602 .pd1 = true,
3603 .pd2 = false,
3604 .psl = false,
3605},
3606.{
3607 .name = "fstack-protector-all",
3608 .syntax = .flag,
3609 .zig_equivalent = .stack_protector,
3610 .pd1 = true,
3611 .pd2 = false,
3612 .psl = false,
3613},
3614.{
3615 .name = "fstack-protector-strong",
3616 .syntax = .flag,
3617 .zig_equivalent = .stack_protector,
3618 .pd1 = true,
3619 .pd2 = false,
3620 .psl = false,
3621},
3594flagpd1("fstack-size-section"),3622flagpd1("fstack-size-section"),
3595flagpd1("fstack-usage"),3623flagpd1("fstack-usage"),
3596flagpd1("fstandalone-debug"),3624flagpd1("fstandalone-debug"),
...@@ -4809,7 +4837,14 @@ flagpd1("single_module"),...@@ -4809,7 +4837,14 @@ flagpd1("single_module"),
4809},4837},
4810sepd1("split-dwarf-file"),4838sepd1("split-dwarf-file"),
4811sepd1("split-dwarf-output"),4839sepd1("split-dwarf-output"),
4812sepd1("stack-protector"),4840.{
4841 .name = "stack-protector",
4842 .syntax = .separate,
4843 .zig_equivalent = .stack_protector,
4844 .pd1 = true,
4845 .pd2 = false,
4846 .psl = false,
4847},
4813sepd1("stack-protector-buffer-size"),4848sepd1("stack-protector-buffer-size"),
4814sepd1("stack-usage-file"),4849sepd1("stack-usage-file"),
4815.{4850.{
src/codegen/llvm.zig+8-3
...@@ -711,9 +711,14 @@ pub const Object = struct {...@@ -711,9 +711,14 @@ pub const Object = struct {
711 DeclGen.removeFnAttr(llvm_func, "noinline");711 DeclGen.removeFnAttr(llvm_func, "noinline");
712 }712 }
713713
714 // TODO: port these over from stage1714 // TODO: disable this if safety is off for the function scope
715 // addLLVMFnAttr(llvm_fn, "sspstrong");715 const ssp_buf_size = module.comp.bin_file.options.stack_protector;
716 // addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4");716 if (ssp_buf_size != 0) {
717 var buf: [12]u8 = undefined;
718 const arg = std.fmt.bufPrintZ(&buf, "{d}", .{ssp_buf_size}) catch unreachable;
719 dg.addFnAttr(llvm_func, "sspstrong");
720 dg.addFnAttrString(llvm_func, "stack-protector-buffer-size", arg);
721 }
717722
718 // TODO: disable this if safety is off for the function scope723 // TODO: disable this if safety is off for the function scope
719 if (module.comp.bin_file.options.stack_check) {724 if (module.comp.bin_file.options.stack_check) {
src/glibc.zig+1
...@@ -1111,6 +1111,7 @@ fn buildSharedLib(...@@ -1111,6 +1111,7 @@ fn buildSharedLib(
1111 .optimize_mode = comp.compilerRtOptMode(),1111 .optimize_mode = comp.compilerRtOptMode(),
1112 .want_sanitize_c = false,1112 .want_sanitize_c = false,
1113 .want_stack_check = false,1113 .want_stack_check = false,
1114 .want_stack_protector = 0,
1114 .want_red_zone = comp.bin_file.options.red_zone,1115 .want_red_zone = comp.bin_file.options.red_zone,
1115 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,1116 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
1116 .want_valgrind = false,1117 .want_valgrind = false,
src/libcxx.zig+2
...@@ -206,6 +206,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {...@@ -206,6 +206,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
206 .link_mode = link_mode,206 .link_mode = link_mode,
207 .want_sanitize_c = false,207 .want_sanitize_c = false,
208 .want_stack_check = false,208 .want_stack_check = false,
209 .want_stack_protector = 0,
209 .want_red_zone = comp.bin_file.options.red_zone,210 .want_red_zone = comp.bin_file.options.red_zone,
210 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,211 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
211 .want_valgrind = false,212 .want_valgrind = false,
...@@ -349,6 +350,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {...@@ -349,6 +350,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
349 .link_mode = link_mode,350 .link_mode = link_mode,
350 .want_sanitize_c = false,351 .want_sanitize_c = false,
351 .want_stack_check = false,352 .want_stack_check = false,
353 .want_stack_protector = 0,
352 .want_red_zone = comp.bin_file.options.red_zone,354 .want_red_zone = comp.bin_file.options.red_zone,
353 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,355 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
354 .want_valgrind = false,356 .want_valgrind = false,
src/libtsan.zig+1
...@@ -211,6 +211,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -211,6 +211,7 @@ pub fn buildTsan(comp: *Compilation) !void {
211 .link_mode = link_mode,211 .link_mode = link_mode,
212 .want_sanitize_c = false,212 .want_sanitize_c = false,
213 .want_stack_check = false,213 .want_stack_check = false,
214 .want_stack_protector = 0,
214 .want_valgrind = false,215 .want_valgrind = false,
215 .want_tsan = false,216 .want_tsan = false,
216 .want_pic = true,217 .want_pic = true,
src/libunwind.zig+1
...@@ -113,6 +113,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {...@@ -113,6 +113,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
113 .link_mode = link_mode,113 .link_mode = link_mode,
114 .want_sanitize_c = false,114 .want_sanitize_c = false,
115 .want_stack_check = false,115 .want_stack_check = false,
116 .want_stack_protector = 0,
116 .want_red_zone = comp.bin_file.options.red_zone,117 .want_red_zone = comp.bin_file.options.red_zone,
117 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,118 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
118 .want_valgrind = false,119 .want_valgrind = false,
src/link.zig+3
...@@ -90,6 +90,9 @@ pub const Options = struct {...@@ -90,6 +90,9 @@ pub const Options = struct {
90 entry: ?[]const u8,90 entry: ?[]const u8,
91 stack_size_override: ?u64,91 stack_size_override: ?u64,
92 image_base_override: ?u64,92 image_base_override: ?u64,
93 /// 0 means no stack protector
94 /// other value means stack protector with that buffer size.
95 stack_protector: u32,
93 cache_mode: CacheMode,96 cache_mode: CacheMode,
94 include_compiler_rt: bool,97 include_compiler_rt: bool,
95 /// Set to `true` to omit debug info.98 /// Set to `true` to omit debug info.
src/link/Elf.zig+6
...@@ -1673,6 +1673,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1673,6 +1673,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1673 }1673 }
1674 }1674 }
16751675
1676 // stack-protector.
1677 // Related: https://github.com/ziglang/zig/issues/7265
1678 if (comp.libssp_static_lib) |ssp| {
1679 try argv.append(ssp.full_object_path);
1680 }
1681
1676 // compiler-rt1682 // compiler-rt
1677 if (compiler_rt_path) |p| {1683 if (compiler_rt_path) |p| {
1678 try argv.append(p);1684 try argv.append(p);
src/main.zig+16
...@@ -378,6 +378,8 @@ const usage_build_generic =...@@ -378,6 +378,8 @@ const usage_build_generic =
378 \\ -fno-lto Force-disable Link Time Optimization378 \\ -fno-lto Force-disable Link Time Optimization
379 \\ -fstack-check Enable stack probing in unsafe builds379 \\ -fstack-check Enable stack probing in unsafe builds
380 \\ -fno-stack-check Disable stack probing in safe builds380 \\ -fno-stack-check Disable stack probing in safe builds
381 \\ -fstack-protector Enable stack protection in unsafe builds
382 \\ -fno-stack-protector Disable stack protection in safe builds
381 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds383 \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
382 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds384 \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
383 \\ -fvalgrind Include valgrind client requests in release builds385 \\ -fvalgrind Include valgrind client requests in release builds
...@@ -668,6 +670,7 @@ fn buildOutputType(...@@ -668,6 +670,7 @@ fn buildOutputType(
668 var want_unwind_tables: ?bool = null;670 var want_unwind_tables: ?bool = null;
669 var want_sanitize_c: ?bool = null;671 var want_sanitize_c: ?bool = null;
670 var want_stack_check: ?bool = null;672 var want_stack_check: ?bool = null;
673 var want_stack_protector: ?u32 = null;
671 var want_red_zone: ?bool = null;674 var want_red_zone: ?bool = null;
672 var omit_frame_pointer: ?bool = null;675 var omit_frame_pointer: ?bool = null;
673 var want_valgrind: ?bool = null;676 var want_valgrind: ?bool = null;
...@@ -1168,6 +1171,10 @@ fn buildOutputType(...@@ -1168,6 +1171,10 @@ fn buildOutputType(
1168 want_stack_check = true;1171 want_stack_check = true;
1169 } else if (mem.eql(u8, arg, "-fno-stack-check")) {1172 } else if (mem.eql(u8, arg, "-fno-stack-check")) {
1170 want_stack_check = false;1173 want_stack_check = false;
1174 } else if (mem.eql(u8, arg, "-fstack-protector")) {
1175 want_stack_protector = Compilation.default_stack_protector_buffer_size;
1176 } else if (mem.eql(u8, arg, "-fno-stack-protector")) {
1177 want_stack_protector = 0;
1171 } else if (mem.eql(u8, arg, "-mred-zone")) {1178 } else if (mem.eql(u8, arg, "-mred-zone")) {
1172 want_red_zone = true;1179 want_red_zone = true;
1173 } else if (mem.eql(u8, arg, "-mno-red-zone")) {1180 } else if (mem.eql(u8, arg, "-mno-red-zone")) {
...@@ -1521,6 +1528,12 @@ fn buildOutputType(...@@ -1521,6 +1528,12 @@ fn buildOutputType(
1521 .no_color_diagnostics => color = .off,1528 .no_color_diagnostics => color = .off,
1522 .stack_check => want_stack_check = true,1529 .stack_check => want_stack_check = true,
1523 .no_stack_check => want_stack_check = false,1530 .no_stack_check => want_stack_check = false,
1531 .stack_protector => {
1532 if (want_stack_protector == null) {
1533 want_stack_protector = Compilation.default_stack_protector_buffer_size;
1534 }
1535 },
1536 .no_stack_protector => want_stack_protector = 0,
1524 .unwind_tables => want_unwind_tables = true,1537 .unwind_tables => want_unwind_tables = true,
1525 .no_unwind_tables => want_unwind_tables = false,1538 .no_unwind_tables => want_unwind_tables = false,
1526 .nostdlib => ensure_libc_on_non_freestanding = false,1539 .nostdlib => ensure_libc_on_non_freestanding = false,
...@@ -2859,6 +2872,7 @@ fn buildOutputType(...@@ -2859,6 +2872,7 @@ fn buildOutputType(
2859 .want_unwind_tables = want_unwind_tables,2872 .want_unwind_tables = want_unwind_tables,
2860 .want_sanitize_c = want_sanitize_c,2873 .want_sanitize_c = want_sanitize_c,
2861 .want_stack_check = want_stack_check,2874 .want_stack_check = want_stack_check,
2875 .want_stack_protector = want_stack_protector,
2862 .want_red_zone = want_red_zone,2876 .want_red_zone = want_red_zone,
2863 .omit_frame_pointer = omit_frame_pointer,2877 .omit_frame_pointer = omit_frame_pointer,
2864 .want_valgrind = want_valgrind,2878 .want_valgrind = want_valgrind,
...@@ -4663,6 +4677,8 @@ pub const ClangArgIterator = struct {...@@ -4663,6 +4677,8 @@ pub const ClangArgIterator = struct {
4663 no_color_diagnostics,4677 no_color_diagnostics,
4664 stack_check,4678 stack_check,
4665 no_stack_check,4679 no_stack_check,
4680 stack_protector,
4681 no_stack_protector,
4666 strip,4682 strip,
4667 exec_model,4683 exec_model,
4668 emit_llvm,4684 emit_llvm,
src/musl.zig+1
...@@ -215,6 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -215,6 +215,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
215 .optimize_mode = comp.compilerRtOptMode(),215 .optimize_mode = comp.compilerRtOptMode(),
216 .want_sanitize_c = false,216 .want_sanitize_c = false,
217 .want_stack_check = false,217 .want_stack_check = false,
218 .want_stack_protector = 0,
218 .want_red_zone = comp.bin_file.options.red_zone,219 .want_red_zone = comp.bin_file.options.red_zone,
219 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,220 .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer,
220 .want_valgrind = false,221 .want_valgrind = false,
src/stage1.zig+1-1
...@@ -416,7 +416,7 @@ export fn stage2_add_link_lib(...@@ -416,7 +416,7 @@ export fn stage2_add_link_lib(
416 const target = comp.getTarget();416 const target = comp.getTarget();
417 const is_libc = target_util.is_libc_lib_name(target, lib_name);417 const is_libc = target_util.is_libc_lib_name(target, lib_name);
418 if (is_libc) {418 if (is_libc) {
419 if (!comp.bin_file.options.link_libc) {419 if (!comp.bin_file.options.link_libc and !comp.bin_file.options.parent_compilation_link_libc) {
420 return "dependency on libc must be explicitly specified in the build command";420 return "dependency on libc must be explicitly specified in the build command";
421 }421 }
422 return null;422 return null;
src/target.zig+9
...@@ -300,6 +300,15 @@ pub fn supportsStackProbing(target: std.Target) bool {...@@ -300,6 +300,15 @@ pub fn supportsStackProbing(target: std.Target) bool {
300 (target.cpu.arch == .i386 or target.cpu.arch == .x86_64);300 (target.cpu.arch == .i386 or target.cpu.arch == .x86_64);
301}301}
302302
303pub fn supportsStackProtector(target: std.Target) bool {
304 // TODO: investigate whether stack-protector works on wasm
305 return !target.isWasm();
306}
307
308pub fn libcProvidesStackProtector(target: std.Target) bool {
309 return !target.isMinGW() and target.os.tag != .wasi;
310}
311
303pub fn supportsReturnAddress(target: std.Target) bool {312pub fn supportsReturnAddress(target: std.Target) bool {
304 return switch (target.cpu.arch) {313 return switch (target.cpu.arch) {
305 .wasm32, .wasm64 => target.os.tag == .emscripten,314 .wasm32, .wasm64 => target.os.tag == .emscripten,
tools/update_clang_options.zig+20
...@@ -352,6 +352,26 @@ const known_options = [_]KnownOpt{...@@ -352,6 +352,26 @@ const known_options = [_]KnownOpt{
352 .name = "fno-stack-check",352 .name = "fno-stack-check",
353 .ident = "no_stack_check",353 .ident = "no_stack_check",
354 },354 },
355 .{
356 .name = "stack-protector",
357 .ident = "stack_protector",
358 },
359 .{
360 .name = "fstack-protector",
361 .ident = "stack_protector",
362 },
363 .{
364 .name = "fno-stack-protector",
365 .ident = "no_stack_protector",
366 },
367 .{
368 .name = "fstack-protector-strong",
369 .ident = "stack_protector",
370 },
371 .{
372 .name = "fstack-protector-all",
373 .ident = "stack_protector",
374 },
355 .{375 .{
356 .name = "MD",376 .name = "MD",
357 .ident = "dep_file",377 .ident = "dep_file",