Commit 3697982
Eric Bower
·
2026-08-02 12:30:28 -0400 EDT
parent 0843272
fix(log): race between checking the length of the file and writing to it
1 files changed,
+13,
-6
+13,
-6
1@@ -1,4 +1,5 @@
2 const std = @import("std");
3+const cross = @import("cross.zig");
4
5 pub var log_system = LogSystem{};
6
7@@ -34,11 +35,17 @@ pub const LogSystem = struct {
8 else => return err,
9 };
10
11- const end_pos = try std.Io.File.length(file, self.io);
12- var buf: [1]u8 = undefined;
13- var w = std.Io.File.writer(file, self.io, &buf);
14- try w.seekTo(end_pos);
15- self.current_size = end_pos;
16+ // Use lseek(SEEK_END) instead of length() + seekTo() to avoid a
17+ // TOCTOU race: after fork() the parent may still write to the log
18+ // between our length() check and seekTo(), causing us to overwrite
19+ // recent parent entries. lseek(fd, 0, SEEK_END) is atomic — it
20+ // always positions at the true end of file at seek time.
21+ const new_pos = cross.c.lseek(file.handle, 0, cross.c.SEEK_END);
22+ if (new_pos == -1) {
23+ std.Io.File.close(file, self.io);
24+ return error.SeekFailed;
25+ }
26+ self.current_size = @as(u64, @intCast(new_pos));
27 self.file = file;
28 }
29
30@@ -73,7 +80,7 @@ pub const LogSystem = struct {
31 const level_name = level.asText();
32
33 const prefix_args = .{
34- now,
35+ now.toSeconds(),
36 level_name,
37 scope_name,
38 };