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
signature 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 {
955955
956956/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
957957/// 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.
958959pub fn execvpeZ_expandArg0(
959960 comptime arg0_expand: Arg0Expand,
960961 file: [*:0]const u8,
......@@ -972,6 +973,14 @@ pub fn execvpeZ_expandArg0(
972973 var it = mem.tokenize(PATH, ":");
973974 var seen_eacces = false;
974975 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
975984 while (it.next()) |search_path| {
976985 if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong;
977986 mem.copy(u8, &path_buf, search_path);
src-self-hosted/libc_installation.zig+117-35
......@@ -174,16 +174,23 @@ pub const LibCInstallation = struct {
174174 });
175175 }
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
177184 /// Finds the default, native libc.
178 pub fn findNative(allocator: *Allocator) FindError!LibCInstallation {
185 pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
179186 var self: LibCInstallation = .{};
180187
181188 if (is_windows) {
182189 if (is_gnu) {
183190 var batch = Batch(FindError!void, 3, .auto_async).init();
184 batch.add(&async self.findNativeIncludeDirPosix(allocator));
185 batch.add(&async self.findNativeCrtDirPosix(allocator));
186 batch.add(&async self.findNativeStaticCrtDirPosix(allocator));
191 batch.add(&async self.findNativeIncludeDirPosix(args));
192 batch.add(&async self.findNativeCrtDirPosix(args));
193 batch.add(&async self.findNativeStaticCrtDirPosix(args));
187194 try batch.wait();
188195 } else {
189196 var sdk: *ZigWindowsSDK = undefined;
......@@ -192,11 +199,11 @@ pub const LibCInstallation = struct {
192199 defer zig_free_windows_sdk(sdk);
193200
194201 var batch = Batch(FindError!void, 5, .auto_async).init();
195 batch.add(&async self.findNativeMsvcIncludeDir(allocator, sdk));
196 batch.add(&async self.findNativeMsvcLibDir(allocator, sdk));
197 batch.add(&async self.findNativeKernel32LibDir(allocator, sdk));
198 batch.add(&async self.findNativeIncludeDirWindows(allocator, sdk));
199 batch.add(&async self.findNativeCrtDirWindows(allocator, sdk));
202 batch.add(&async self.findNativeMsvcIncludeDir(args, sdk));
203 batch.add(&async self.findNativeMsvcLibDir(args, sdk));
204 batch.add(&async self.findNativeKernel32LibDir(args, sdk));
205 batch.add(&async self.findNativeIncludeDirWindows(args, sdk));
206 batch.add(&async self.findNativeCrtDirWindows(args, sdk));
200207 try batch.wait();
201208 },
202209 .OutOfMemory => return error.OutOfMemory,
......@@ -208,11 +215,11 @@ pub const LibCInstallation = struct {
208215 try blk: {
209216 var batch = Batch(FindError!void, 2, .auto_async).init();
210217 errdefer batch.wait() catch {};
211 batch.add(&async self.findNativeIncludeDirPosix(allocator));
218 batch.add(&async self.findNativeIncludeDirPosix(args));
212219 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");
214221 } else if (is_linux or is_dragonfly) {
215 batch.add(&async self.findNativeCrtDirPosix(allocator));
222 batch.add(&async self.findNativeCrtDirPosix(args));
216223 }
217224 break :blk batch.wait();
218225 };
......@@ -231,7 +238,8 @@ pub const LibCInstallation = struct {
231238 self.* = undefined;
232239 }
233240
234 fn findNativeIncludeDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
241 fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
242 const allocator = args.allocator;
235243 const dev_null = if (is_windows) "nul" else "/dev/null";
236244 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
237245 const argv = [_][]const u8{
......@@ -252,15 +260,24 @@ pub const LibCInstallation = struct {
252260 .expand_arg0 = .expand,
253261 }) catch |err| switch (err) {
254262 error.OutOfMemory => return error.OutOfMemory,
255 else => return error.UnableToSpawnCCompiler,
263 else => {
264 printVerboseInvocation(&argv, null, args.verbose, null);
265 return error.UnableToSpawnCCompiler;
266 },
256267 };
257268 defer {
258269 allocator.free(exec_res.stdout);
259270 allocator.free(exec_res.stderr);
260271 }
261272 switch (exec_res.term) {
262 .Exited => |code| if (code != 0) return error.CCompilerExitCode,
263 else => return error.CCompilerCrashed,
273 .Exited => |code| if (code != 0) {
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 },
264281 }
265282
266283 var it = std.mem.tokenize(exec_res.stderr, "\n\r");
......@@ -322,9 +339,11 @@ pub const LibCInstallation = struct {
322339
323340 fn findNativeIncludeDirWindows(
324341 self: *LibCInstallation,
325 allocator: *Allocator,
342 args: FindNativeOptions,
326343 sdk: *ZigWindowsSDK,
327344 ) FindError!void {
345 const allocator = args.allocator;
346
328347 var search_buf: [2]Search = undefined;
329348 const searches = fillSearch(&search_buf, sdk);
330349
......@@ -358,7 +377,13 @@ pub const LibCInstallation = struct {
358377 return error.LibCStdLibHeaderNotFound;
359378 }
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
362387 var search_buf: [2]Search = undefined;
363388 const searches = fillSearch(&search_buf, sdk);
364389
......@@ -398,15 +423,31 @@ pub const LibCInstallation = struct {
398423 return error.LibCRuntimeNotFound;
399424 }
400425
401 fn findNativeCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
402 self.crt_dir = try ccPrintFileName(allocator, "crt1.o", .only_dir);
426 fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
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 });
403433 }
404434
405 fn findNativeStaticCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
406 self.static_crt_dir = try ccPrintFileName(allocator, "crtbegin.o", .only_dir);
435 fn findNativeStaticCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
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 });
407442 }
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
410451 var search_buf: [2]Search = undefined;
411452 const searches = fillSearch(&search_buf, sdk);
412453
......@@ -448,9 +489,11 @@ pub const LibCInstallation = struct {
448489
449490 fn findNativeMsvcIncludeDir(
450491 self: *LibCInstallation,
451 allocator: *Allocator,
492 args: FindNativeOptions,
452493 sdk: *ZigWindowsSDK,
453494 ) FindError!void {
495 const allocator = args.allocator;
496
454497 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;
455498 const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];
456499 const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
......@@ -481,9 +524,10 @@ pub const LibCInstallation = struct {
481524
482525 fn findNativeMsvcLibDir(
483526 self: *LibCInstallation,
484 allocator: *Allocator,
527 args: FindNativeOptions,
485528 sdk: *ZigWindowsSDK,
486529 ) FindError!void {
530 const allocator = args.allocator;
487531 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;
488532 self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
489533 }
......@@ -491,14 +535,19 @@ pub const LibCInstallation = struct {
491535
492536const default_cc_exe = if (is_windows) "cc.exe" else "cc";
493537
494/// caller owns returned memory
495fn ccPrintFileName(
538pub const CCPrintFileNameOptions = struct {
496539 allocator: *Allocator,
497 o_file: []const u8,
540 search_basename: []const u8,
498541 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
500549 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});
502551 defer allocator.free(arg1);
503552 const argv = [_][]const u8{ cc_exe, arg1 };
504553
......@@ -520,16 +569,22 @@ fn ccPrintFileName(
520569 allocator.free(exec_res.stderr);
521570 }
522571 switch (exec_res.term) {
523 .Exited => |code| if (code != 0) return error.CCompilerExitCode,
524 else => return error.CCompilerCrashed,
572 .Exited => |code| if (code != 0) {
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 },
525580 }
526581
527582 var it = std.mem.tokenize(exec_res.stdout, "\n\r");
528583 const line = it.next() orelse return error.LibCRuntimeNotFound;
529584 // When this command fails, it returns exit code 0 and duplicates the input file name.
530585 // 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;
532 switch (want_dirname) {
586 if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
587 switch (args.want_dirname) {
533588 .full_path => return std.mem.dupeZ(allocator, u8, line),
534589 .only_dir => {
535590 const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
......@@ -538,6 +593,29 @@ fn ccPrintFileName(
538593 }
539594}
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
541619/// Caller owns returned memory.
542620pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
543621 OutOfMemory,
......@@ -617,7 +695,11 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
617695 for (ld_info_list.toSlice()) |ld_info| {
618696 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) {
621703 error.OutOfMemory => return error.OutOfMemory,
622704 error.LibCRuntimeNotFound,
623705 error.CCompilerExitCode,
src-self-hosted/stage2.zig+4-1
......@@ -899,7 +899,10 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
899899
900900// ABI warning
901901export 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) {
903906 error.OutOfMemory => return .OutOfMemory,
904907 error.FileSystem => return .FileSystem,
905908 error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,