diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-12-26 13:50:26 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-12-26 13:50:26 -0700 |
| commit | 3f9588ca29c721f11eb9dae8f6de8ebd1155c7bf (patch) | |
| tree | cef7fd69c3dc20dabdda4f90d3a7fc5b2c46d13f /lib/std/os.zig | |
| parent | ed39ff202baf5bb73e54f7ecc20df63d9c190dc9 (diff) | |
| download | zig-3f9588ca29c721f11eb9dae8f6de8ebd1155c7bf.tar.gz zig-3f9588ca29c721f11eb9dae8f6de8ebd1155c7bf.zip | |
std: do not call malloc() between fork() and execv()
We were violating the POSIX standard which resulted in a deadlock on
musl v1.1.24 on aarch64 alpine linux, uncovered with the new ThreadPool
usage in the stage2 compiler.
std.os execv functions that accept an Allocator parameter are removed
because they are footguns. The POSIX standard does not allow calls to
malloc() between fork() and execv() and since it is common to both
(1) call execv() after fork() and (2) use std.heap.c_allocator,
Programmers are encouraged to go through the `std.process` API
instead, causing some dissonance when combined with `std.os` APIs.
I also slapped a big warning message on all the relevant doc comments.
Diffstat (limited to 'lib/std/os.zig')
| -rw-r--r-- | lib/std/os.zig | 83 |
1 files changed, 2 insertions, 81 deletions
diff --git a/lib/std/os.zig b/lib/std/os.zig index 5a57fed5cf..28d4f6bb1a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1348,89 +1348,10 @@ pub fn execvpeZ_expandArg0( /// If `file` is an absolute path, this is the same as `execveZ`. pub fn execvpeZ( file: [*:0]const u8, - argv: [*:null]const ?[*:0]const u8, + argv_ptr: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8, ) ExecveError { - return execvpeZ_expandArg0(.no_expand, file, argv, envp); -} - -/// This is the same as `execvpe` except if the `arg0_expand` parameter is set to `.expand`, -/// then argv[0] will be replaced with the expanded version of it, after resolving in accordance -/// with the PATH environment variable. -pub fn execvpe_expandArg0( - allocator: *mem.Allocator, - arg0_expand: Arg0Expand, - argv_slice: []const []const u8, - env_map: *const std.BufMap, -) (ExecveError || error{OutOfMemory}) { - const argv_buf = try allocator.alloc(?[*:0]u8, argv_slice.len + 1); - mem.set(?[*:0]u8, argv_buf, null); - defer { - for (argv_buf) |arg| { - const arg_buf = mem.spanZ(arg) orelse break; - allocator.free(arg_buf); - } - allocator.free(argv_buf); - } - for (argv_slice) |arg, i| { - const arg_buf = try allocator.alloc(u8, arg.len + 1); - @memcpy(arg_buf.ptr, arg.ptr, arg.len); - arg_buf[arg.len] = 0; - argv_buf[i] = arg_buf[0..arg.len :0].ptr; - } - argv_buf[argv_slice.len] = null; - const argv_ptr = argv_buf[0..argv_slice.len :null].ptr; - - const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); - defer freeNullDelimitedEnvMap(allocator, envp_buf); - - switch (arg0_expand) { - .expand => return execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr), - .no_expand => return execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr), - } -} - -/// This function must allocate memory to add a null terminating bytes on path and each arg. -/// It must also convert to KEY=VALUE\0 format for environment variables, and include null -/// pointers after the args and after the environment variables. -/// `argv_slice[0]` is the executable path. -/// This function also uses the PATH environment variable to get the full path to the executable. -pub fn execvpe( - allocator: *mem.Allocator, - argv_slice: []const []const u8, - env_map: *const std.BufMap, -) (ExecveError || error{OutOfMemory}) { - return execvpe_expandArg0(allocator, .no_expand, argv_slice, env_map); -} - -pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 { - const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?[*:0]u8, envp_count + 1); - mem.set(?[*:0]u8, envp_buf, null); - errdefer freeNullDelimitedEnvMap(allocator, envp_buf); - { - var it = env_map.iterator(); - var i: usize = 0; - while (it.next()) |pair| : (i += 1) { - const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); - @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); - env_buf[pair.key.len] = '='; - @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); - const len = env_buf.len - 1; - env_buf[len] = 0; - envp_buf[i] = env_buf[0..len :0].ptr; - } - assert(i == envp_count); - } - return envp_buf[0..envp_count :null]; -} - -pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void { - for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. mem.len(ptr) + 1] else break; - allocator.free(env_buf); - } - allocator.free(envp_buf); + return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp); } /// Get an environment variable. |
