authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-22 13:43:48-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-22 13:56:02-05:00
log0cd89e9176ab36fc5e267120dc4d75cb79d32684
treec2109735be89fdaf86b55a95d98a4797d086217c
parentdca19b67573dc46260b318e9253370fbc628834d
signaturelock-open Commit is signed but in an unrecognized format.

std.os.execvpeZ_expandArg0: fix not restoring argv[0]

This function expands argv[0] into the absolute path resolved with PATH environment variable before making the execve syscall. However, in case the execve fails, e.g. with ENOENT, it did not restore argv to how it was before it was passed in. This resulted in the caller performing an invalid free. This commit also adds verbose debug info when native system C compiler detection fails. See #4521.

3 files changed, 130 insertions(+), 36 deletions(-)

lib/std/os.zig+9
...@@ -955,6 +955,7 @@ pub const Arg0Expand = enum {...@@ -955,6 +955,7 @@ pub const Arg0Expand = enum {
955955
956/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,956/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
957/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.957/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
958/// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in.
958pub fn execvpeZ_expandArg0(959pub fn execvpeZ_expandArg0(
959 comptime arg0_expand: Arg0Expand,960 comptime arg0_expand: Arg0Expand,
960 file: [*:0]const u8,961 file: [*:0]const u8,
...@@ -972,6 +973,14 @@ pub fn execvpeZ_expandArg0(...@@ -972,6 +973,14 @@ pub fn execvpeZ_expandArg0(
972 var it = mem.tokenize(PATH, ":");973 var it = mem.tokenize(PATH, ":");
973 var seen_eacces = false;974 var seen_eacces = false;
974 var err: ExecveError = undefined;975 var err: ExecveError = undefined;
976
977 // In case of expanding arg0 we must put it back if we return with an error.
978 const prev_arg0 = child_argv[0];
979 defer switch (arg0_expand) {
980 .expand => child_argv[0] = prev_arg0,
981 .no_expand => {},
982 };
983
975 while (it.next()) |search_path| {984 while (it.next()) |search_path| {
976 if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong;985 if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong;
977 mem.copy(u8, &path_buf, search_path);986 mem.copy(u8, &path_buf, search_path);
src-self-hosted/libc_installation.zig+117-35
...@@ -174,16 +174,23 @@ pub const LibCInstallation = struct {...@@ -174,16 +174,23 @@ pub const LibCInstallation = struct {
174 });174 });
175 }175 }
176176
177 pub const FindNativeOptions = struct {
178 allocator: *Allocator,
179
180 /// If enabled, will print human-friendly errors to stderr.
181 verbose: bool = false,
182 };
183
177 /// Finds the default, native libc.184 /// Finds the default, native libc.
178 pub fn findNative(allocator: *Allocator) FindError!LibCInstallation {185 pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
179 var self: LibCInstallation = .{};186 var self: LibCInstallation = .{};
180187
181 if (is_windows) {188 if (is_windows) {
182 if (is_gnu) {189 if (is_gnu) {
183 var batch = Batch(FindError!void, 3, .auto_async).init();190 var batch = Batch(FindError!void, 3, .auto_async).init();
184 batch.add(&async self.findNativeIncludeDirPosix(allocator));191 batch.add(&async self.findNativeIncludeDirPosix(args));
185 batch.add(&async self.findNativeCrtDirPosix(allocator));192 batch.add(&async self.findNativeCrtDirPosix(args));
186 batch.add(&async self.findNativeStaticCrtDirPosix(allocator));193 batch.add(&async self.findNativeStaticCrtDirPosix(args));
187 try batch.wait();194 try batch.wait();
188 } else {195 } else {
189 var sdk: *ZigWindowsSDK = undefined;196 var sdk: *ZigWindowsSDK = undefined;
...@@ -192,11 +199,11 @@ pub const LibCInstallation = struct {...@@ -192,11 +199,11 @@ pub const LibCInstallation = struct {
192 defer zig_free_windows_sdk(sdk);199 defer zig_free_windows_sdk(sdk);
193200
194 var batch = Batch(FindError!void, 5, .auto_async).init();201 var batch = Batch(FindError!void, 5, .auto_async).init();
195 batch.add(&async self.findNativeMsvcIncludeDir(allocator, sdk));202 batch.add(&async self.findNativeMsvcIncludeDir(args, sdk));
196 batch.add(&async self.findNativeMsvcLibDir(allocator, sdk));203 batch.add(&async self.findNativeMsvcLibDir(args, sdk));
197 batch.add(&async self.findNativeKernel32LibDir(allocator, sdk));204 batch.add(&async self.findNativeKernel32LibDir(args, sdk));
198 batch.add(&async self.findNativeIncludeDirWindows(allocator, sdk));205 batch.add(&async self.findNativeIncludeDirWindows(args, sdk));
199 batch.add(&async self.findNativeCrtDirWindows(allocator, sdk));206 batch.add(&async self.findNativeCrtDirWindows(args, sdk));
200 try batch.wait();207 try batch.wait();
201 },208 },
202 .OutOfMemory => return error.OutOfMemory,209 .OutOfMemory => return error.OutOfMemory,
...@@ -208,11 +215,11 @@ pub const LibCInstallation = struct {...@@ -208,11 +215,11 @@ pub const LibCInstallation = struct {
208 try blk: {215 try blk: {
209 var batch = Batch(FindError!void, 2, .auto_async).init();216 var batch = Batch(FindError!void, 2, .auto_async).init();
210 errdefer batch.wait() catch {};217 errdefer batch.wait() catch {};
211 batch.add(&async self.findNativeIncludeDirPosix(allocator));218 batch.add(&async self.findNativeIncludeDirPosix(args));
212 if (is_freebsd or is_netbsd) {219 if (is_freebsd or is_netbsd) {
213 self.crt_dir = try std.mem.dupeZ(allocator, u8, "/usr/lib");220 self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib");
214 } else if (is_linux or is_dragonfly) {221 } else if (is_linux or is_dragonfly) {
215 batch.add(&async self.findNativeCrtDirPosix(allocator));222 batch.add(&async self.findNativeCrtDirPosix(args));
216 }223 }
217 break :blk batch.wait();224 break :blk batch.wait();
218 };225 };
...@@ -231,7 +238,8 @@ pub const LibCInstallation = struct {...@@ -231,7 +238,8 @@ pub const LibCInstallation = struct {
231 self.* = undefined;238 self.* = undefined;
232 }239 }
233240
234 fn findNativeIncludeDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {241 fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
242 const allocator = args.allocator;
235 const dev_null = if (is_windows) "nul" else "/dev/null";243 const dev_null = if (is_windows) "nul" else "/dev/null";
236 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;244 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
237 const argv = [_][]const u8{245 const argv = [_][]const u8{
...@@ -252,15 +260,24 @@ pub const LibCInstallation = struct {...@@ -252,15 +260,24 @@ pub const LibCInstallation = struct {
252 .expand_arg0 = .expand,260 .expand_arg0 = .expand,
253 }) catch |err| switch (err) {261 }) catch |err| switch (err) {
254 error.OutOfMemory => return error.OutOfMemory,262 error.OutOfMemory => return error.OutOfMemory,
255 else => return error.UnableToSpawnCCompiler,263 else => {
264 printVerboseInvocation(&argv, null, args.verbose, null);
265 return error.UnableToSpawnCCompiler;
266 },
256 };267 };
257 defer {268 defer {
258 allocator.free(exec_res.stdout);269 allocator.free(exec_res.stdout);
259 allocator.free(exec_res.stderr);270 allocator.free(exec_res.stderr);
260 }271 }
261 switch (exec_res.term) {272 switch (exec_res.term) {
262 .Exited => |code| if (code != 0) return error.CCompilerExitCode,273 .Exited => |code| if (code != 0) {
263 else => return error.CCompilerCrashed,274 printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
275 return error.CCompilerExitCode;
276 },
277 else => {
278 printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
279 return error.CCompilerCrashed;
280 },
264 }281 }
265282
266 var it = std.mem.tokenize(exec_res.stderr, "\n\r");283 var it = std.mem.tokenize(exec_res.stderr, "\n\r");
...@@ -322,9 +339,11 @@ pub const LibCInstallation = struct {...@@ -322,9 +339,11 @@ pub const LibCInstallation = struct {
322339
323 fn findNativeIncludeDirWindows(340 fn findNativeIncludeDirWindows(
324 self: *LibCInstallation,341 self: *LibCInstallation,
325 allocator: *Allocator,342 args: FindNativeOptions,
326 sdk: *ZigWindowsSDK,343 sdk: *ZigWindowsSDK,
327 ) FindError!void {344 ) FindError!void {
345 const allocator = args.allocator;
346
328 var search_buf: [2]Search = undefined;347 var search_buf: [2]Search = undefined;
329 const searches = fillSearch(&search_buf, sdk);348 const searches = fillSearch(&search_buf, sdk);
330349
...@@ -358,7 +377,13 @@ pub const LibCInstallation = struct {...@@ -358,7 +377,13 @@ pub const LibCInstallation = struct {
358 return error.LibCStdLibHeaderNotFound;377 return error.LibCStdLibHeaderNotFound;
359 }378 }
360379
361 fn findNativeCrtDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void {380 fn findNativeCrtDirWindows(
381 self: *LibCInstallation,
382 args: FindNativeOptions,
383 sdk: *ZigWindowsSDK,
384 ) FindError!void {
385 const allocator = args.allocator;
386
362 var search_buf: [2]Search = undefined;387 var search_buf: [2]Search = undefined;
363 const searches = fillSearch(&search_buf, sdk);388 const searches = fillSearch(&search_buf, sdk);
364389
...@@ -398,15 +423,31 @@ pub const LibCInstallation = struct {...@@ -398,15 +423,31 @@ pub const LibCInstallation = struct {
398 return error.LibCRuntimeNotFound;423 return error.LibCRuntimeNotFound;
399 }424 }
400425
401 fn findNativeCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {426 fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
402 self.crt_dir = try ccPrintFileName(allocator, "crt1.o", .only_dir);427 self.crt_dir = try ccPrintFileName(.{
428 .allocator = args.allocator,
429 .search_basename = "crt1.o",
430 .want_dirname = .only_dir,
431 .verbose = args.verbose,
432 });
403 }433 }
404434
405 fn findNativeStaticCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {435 fn findNativeStaticCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
406 self.static_crt_dir = try ccPrintFileName(allocator, "crtbegin.o", .only_dir);436 self.static_crt_dir = try ccPrintFileName(.{
437 .allocator = args.allocator,
438 .search_basename = "crtbegin.o",
439 .want_dirname = .only_dir,
440 .verbose = args.verbose,
441 });
407 }442 }
408443
409 fn findNativeKernel32LibDir(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void {444 fn findNativeKernel32LibDir(
445 self: *LibCInstallation,
446 args: FindNativeOptions,
447 sdk: *ZigWindowsSDK,
448 ) FindError!void {
449 const allocator = args.allocator;
450
410 var search_buf: [2]Search = undefined;451 var search_buf: [2]Search = undefined;
411 const searches = fillSearch(&search_buf, sdk);452 const searches = fillSearch(&search_buf, sdk);
412453
...@@ -448,9 +489,11 @@ pub const LibCInstallation = struct {...@@ -448,9 +489,11 @@ pub const LibCInstallation = struct {
448489
449 fn findNativeMsvcIncludeDir(490 fn findNativeMsvcIncludeDir(
450 self: *LibCInstallation,491 self: *LibCInstallation,
451 allocator: *Allocator,492 args: FindNativeOptions,
452 sdk: *ZigWindowsSDK,493 sdk: *ZigWindowsSDK,
453 ) FindError!void {494 ) FindError!void {
495 const allocator = args.allocator;
496
454 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;497 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;
455 const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];498 const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];
456 const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;499 const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
...@@ -481,9 +524,10 @@ pub const LibCInstallation = struct {...@@ -481,9 +524,10 @@ pub const LibCInstallation = struct {
481524
482 fn findNativeMsvcLibDir(525 fn findNativeMsvcLibDir(
483 self: *LibCInstallation,526 self: *LibCInstallation,
484 allocator: *Allocator,527 args: FindNativeOptions,
485 sdk: *ZigWindowsSDK,528 sdk: *ZigWindowsSDK,
486 ) FindError!void {529 ) FindError!void {
530 const allocator = args.allocator;
487 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;531 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;
488 self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);532 self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
489 }533 }
...@@ -491,14 +535,19 @@ pub const LibCInstallation = struct {...@@ -491,14 +535,19 @@ pub const LibCInstallation = struct {
491535
492const default_cc_exe = if (is_windows) "cc.exe" else "cc";536const default_cc_exe = if (is_windows) "cc.exe" else "cc";
493537
494/// caller owns returned memory538pub const CCPrintFileNameOptions = struct {
495fn ccPrintFileName(
496 allocator: *Allocator,539 allocator: *Allocator,
497 o_file: []const u8,540 search_basename: []const u8,
498 want_dirname: enum { full_path, only_dir },541 want_dirname: enum { full_path, only_dir },
499) ![:0]u8 {542 verbose: bool = false,
543};
544
545/// caller owns returned memory
546fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
547 const allocator = args.allocator;
548
500 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;549 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
501 const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{o_file});550 const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename});
502 defer allocator.free(arg1);551 defer allocator.free(arg1);
503 const argv = [_][]const u8{ cc_exe, arg1 };552 const argv = [_][]const u8{ cc_exe, arg1 };
504553
...@@ -520,16 +569,22 @@ fn ccPrintFileName(...@@ -520,16 +569,22 @@ fn ccPrintFileName(
520 allocator.free(exec_res.stderr);569 allocator.free(exec_res.stderr);
521 }570 }
522 switch (exec_res.term) {571 switch (exec_res.term) {
523 .Exited => |code| if (code != 0) return error.CCompilerExitCode,572 .Exited => |code| if (code != 0) {
524 else => return error.CCompilerCrashed,573 printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
574 return error.CCompilerExitCode;
575 },
576 else => {
577 printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
578 return error.CCompilerCrashed;
579 },
525 }580 }
526581
527 var it = std.mem.tokenize(exec_res.stdout, "\n\r");582 var it = std.mem.tokenize(exec_res.stdout, "\n\r");
528 const line = it.next() orelse return error.LibCRuntimeNotFound;583 const line = it.next() orelse return error.LibCRuntimeNotFound;
529 // When this command fails, it returns exit code 0 and duplicates the input file name.584 // When this command fails, it returns exit code 0 and duplicates the input file name.
530 // So we detect failure by checking if the output matches exactly the input.585 // So we detect failure by checking if the output matches exactly the input.
531 if (std.mem.eql(u8, line, o_file)) return error.LibCRuntimeNotFound;586 if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
532 switch (want_dirname) {587 switch (args.want_dirname) {
533 .full_path => return std.mem.dupeZ(allocator, u8, line),588 .full_path => return std.mem.dupeZ(allocator, u8, line),
534 .only_dir => {589 .only_dir => {
535 const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;590 const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
...@@ -538,6 +593,29 @@ fn ccPrintFileName(...@@ -538,6 +593,29 @@ fn ccPrintFileName(
538 }593 }
539}594}
540595
596fn printVerboseInvocation(
597 argv: []const []const u8,
598 search_basename: ?[]const u8,
599 verbose: bool,
600 stderr: ?[]const u8,
601) void {
602 if (!verbose) return;
603
604 if (search_basename) |s| {
605 std.debug.warn("Zig attempted to find the file '{}' by executing this command:\n", .{s});
606 } else {
607 std.debug.warn("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
608 }
609 for (argv) |arg, i| {
610 if (i != 0) std.debug.warn(" ", .{});
611 std.debug.warn("{}", .{arg});
612 }
613 std.debug.warn("\n", .{});
614 if (stderr) |s| {
615 std.debug.warn("Output:\n==========\n{}\n==========\n", .{s});
616 }
617}
618
541/// Caller owns returned memory.619/// Caller owns returned memory.
542pub fn detectNativeDynamicLinker(allocator: *Allocator) error{620pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
543 OutOfMemory,621 OutOfMemory,
...@@ -617,7 +695,11 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{...@@ -617,7 +695,11 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
617 for (ld_info_list.toSlice()) |ld_info| {695 for (ld_info_list.toSlice()) |ld_info| {
618 const standard_ld_basename = fs.path.basename(ld_info.ld_path);696 const standard_ld_basename = fs.path.basename(ld_info.ld_path);
619697
620 const full_ld_path = ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {698 const full_ld_path = ccPrintFileName(.{
699 .allocator = allocator,
700 .search_basename = standard_ld_basename,
701 .want_dirname = .full_path,
702 }) catch |err| switch (err) {
621 error.OutOfMemory => return error.OutOfMemory,703 error.OutOfMemory => return error.OutOfMemory,
622 error.LibCRuntimeNotFound,704 error.LibCRuntimeNotFound,
623 error.CCompilerExitCode,705 error.CCompilerExitCode,
src-self-hosted/stage2.zig+4-1
...@@ -899,7 +899,10 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [...@@ -899,7 +899,10 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
899899
900// ABI warning900// ABI warning
901export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {901export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
902 var libc = LibCInstallation.findNative(std.heap.c_allocator) catch |err| switch (err) {902 var libc = LibCInstallation.findNative(.{
903 .allocator = std.heap.c_allocator,
904 .verbose = true,
905 }) catch |err| switch (err) {
903 error.OutOfMemory => return .OutOfMemory,906 error.OutOfMemory => return .OutOfMemory,
904 error.FileSystem => return .FileSystem,907 error.FileSystem => return .FileSystem,
905 error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,908 error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,