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,4 +1,5 @@
11 const std = @import("std");
2+const cross = @import("cross.zig");
23
34 pub var log_system = LogSystem{};
45
......@@ -34,11 +35,17 @@ pub const LogSystem = struct {
3435 else => return err,
3536 };
3637
37- const end_pos = try std.Io.File.length(file, self.io);
38- var buf: [1]u8 = undefined;
39- var w = std.Io.File.writer(file, self.io, &buf);
40- try w.seekTo(end_pos);
41- self.current_size = end_pos;
38+ // Use lseek(SEEK_END) instead of length() + seekTo() to avoid a
39+ // TOCTOU race: after fork() the parent may still write to the log
40+ // between our length() check and seekTo(), causing us to overwrite
41+ // recent parent entries. lseek(fd, 0, SEEK_END) is atomic — it
42+ // always positions at the true end of file at seek time.
43+ const new_pos = cross.c.lseek(file.handle, 0, cross.c.SEEK_END);
44+ if (new_pos == -1) {
45+ std.Io.File.close(file, self.io);
46+ return error.SeekFailed;
47+ }
48+ self.current_size = @as(u64, @intCast(new_pos));
4249 self.file = file;
4350 }
4451
......@@ -73,7 +80,7 @@ pub const LogSystem = struct {
7380 const level_name = level.asText();
7481
7582 const prefix_args = .{
76- now,
83+ now.toSeconds(),
7784 level_name,
7885 scope_name,
7986 };