authorgravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2025-05-26 19:23:42+00:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-08-26 17:56:01+02:00
log628357eeb6fd017bd374e3eff3a3c506244559d9
tree64ab4cc6d2d2cd9f3dbb343a78489825a72b2474
parentcaa89c7601bba7520bae0e295cce1b7db896041c
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

std.Build.Step.Compile: fix race condition in args file creation

Fixes #23993 Previously, if multiple build processes tried to create the same args file, there was a race condition with the use of the non-atomic `writeFile` function which could cause a spawned compiler to read an empty or incomplete args file. This commit avoids the race condition by first writing to a temporary file with a random path and renaming it to the desired path.

1 files changed, 20 insertions(+), 1 deletions(-)

lib/std/Build/Step/Compile.zig+20-1
...@@ -1827,7 +1827,26 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1827,7 +1827,26 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1827 _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});1827 _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});
18281828
1829 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;1829 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
1830 try b.cache_root.handle.writeFile(.{ .sub_path = args_file, .data = args });1830 if (b.cache_root.handle.access(args_file, .{})) |_| {
1831 // The args file is already present from a previous run.
1832 } else |err| switch (err) {
1833 error.FileNotFound => {
1834 try b.cache_root.handle.makePath("tmp");
1835 const rand_int = std.crypto.random.int(u64);
1836 const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
1837 try b.cache_root.handle.writeFile(.{ .sub_path = tmp_path, .data = args });
1838 defer b.cache_root.handle.deleteFile(tmp_path) catch {
1839 // It's fine if the temporary file can't be cleaned up.
1840 };
1841 b.cache_root.handle.rename(tmp_path, args_file) catch |rename_err| switch (rename_err) {
1842 error.PathAlreadyExists => {
1843 // The args file was created by another concurrent build process.
1844 },
1845 else => |other_err| return other_err,
1846 };
1847 },
1848 else => |other_err| return other_err,
1849 }
18311850
1832 const resolved_args_file = try mem.concat(arena, u8, &.{1851 const resolved_args_file = try mem.concat(arena, u8, &.{
1833 "@",1852 "@",