fix(kqueue): copy watch path under lock in thread_fn to prevent use-after-free
This commit is contained in:
parent
c4bb73dfe1
commit
2e7838515b
2 changed files with 43 additions and 21 deletions
|
|
@ -119,32 +119,49 @@ fn thread_fn(self: *@This(), allocator: std.mem.Allocator) void {
|
|||
const fd: std.posix.fd_t = @intCast(ev.ident);
|
||||
|
||||
// Check if this is a file watch: NOTE_WRITE/NOTE_EXTEND → modified.
|
||||
// Copy the path under the lock so a concurrent remove_watch cannot
|
||||
// free it before we finish using it.
|
||||
var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var file_path_len: usize = 0;
|
||||
self.file_watches_mutex.lock();
|
||||
var fwit = self.file_watches.iterator();
|
||||
const file_path: ?[]const u8 = while (fwit.next()) |entry| {
|
||||
if (entry.value_ptr.* == fd) break entry.key_ptr.*;
|
||||
} else null;
|
||||
while (fwit.next()) |entry| {
|
||||
if (entry.value_ptr.* == fd) {
|
||||
@memcpy(file_path_buf[0..entry.key_ptr.*.len], entry.key_ptr.*);
|
||||
file_path_len = entry.key_ptr.*.len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.file_watches_mutex.unlock();
|
||||
if (file_path) |fp| {
|
||||
if (file_path_len > 0) {
|
||||
const fp = file_path_buf[0..file_path_len];
|
||||
if (ev.fflags & (NOTE_WRITE | NOTE_EXTEND) != 0)
|
||||
self.handler.change(fp, EventType.modified, .file) catch return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise look up the directory path for this fd.
|
||||
// Same copy-under-lock pattern.
|
||||
var dir_path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var dir_path_len: usize = 0;
|
||||
self.watches_mutex.lock();
|
||||
var wit = self.watches.iterator();
|
||||
const dir_path: ?[]const u8 = while (wit.next()) |entry| {
|
||||
if (entry.value_ptr.* == fd) break entry.key_ptr.*;
|
||||
} else null;
|
||||
while (wit.next()) |entry| {
|
||||
if (entry.value_ptr.* == fd) {
|
||||
@memcpy(dir_path_buf[0..entry.key_ptr.*.len], entry.key_ptr.*);
|
||||
dir_path_len = entry.key_ptr.*.len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.watches_mutex.unlock();
|
||||
if (dir_path == null) continue;
|
||||
if (dir_path_len == 0) continue;
|
||||
const dir_path = dir_path_buf[0..dir_path_len];
|
||||
if (ev.fflags & NOTE_DELETE != 0) {
|
||||
self.handler.change(dir_path.?, EventType.deleted, .dir) catch return;
|
||||
self.handler.change(dir_path, EventType.deleted, .dir) catch return;
|
||||
} else if (ev.fflags & NOTE_RENAME != 0) {
|
||||
self.handler.change(dir_path.?, EventType.renamed, .dir) catch return;
|
||||
self.handler.change(dir_path, EventType.renamed, .dir) catch return;
|
||||
} else if (ev.fflags & NOTE_WRITE != 0) {
|
||||
self.scan_dir(allocator, dir_path.?) catch {};
|
||||
self.scan_dir(allocator, dir_path) catch {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,35 +103,40 @@ fn thread_fn(self: *@This(), allocator: std.mem.Allocator) void {
|
|||
if (ev.filter != EVFILT_VNODE) continue;
|
||||
const fd: std.posix.fd_t = @intCast(ev.ident);
|
||||
|
||||
// Copy the path under the lock so a concurrent remove_watch cannot
|
||||
// free it before we finish using it.
|
||||
var watch_path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var watch_path_len: usize = 0;
|
||||
var is_file: bool = false;
|
||||
self.watches_mutex.lock();
|
||||
var wit = self.watches.iterator();
|
||||
var watch_path: ?[]const u8 = null;
|
||||
var is_file: bool = false;
|
||||
while (wit.next()) |entry| {
|
||||
if (entry.value_ptr.*.fd == fd) {
|
||||
watch_path = entry.key_ptr.*;
|
||||
@memcpy(watch_path_buf[0..entry.key_ptr.*.len], entry.key_ptr.*);
|
||||
watch_path_len = entry.key_ptr.*.len;
|
||||
is_file = entry.value_ptr.*.is_file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.watches_mutex.unlock();
|
||||
if (watch_path == null) continue;
|
||||
if (watch_path_len == 0) continue;
|
||||
const watch_path = watch_path_buf[0..watch_path_len];
|
||||
if (is_file) {
|
||||
// Explicit file watch: emit events with .file type directly.
|
||||
if (ev.fflags & NOTE_DELETE != 0) {
|
||||
self.handler.change(watch_path.?, EventType.deleted, .file) catch return;
|
||||
self.handler.change(watch_path, EventType.deleted, .file) catch return;
|
||||
} else if (ev.fflags & NOTE_RENAME != 0) {
|
||||
self.handler.change(watch_path.?, EventType.renamed, .file) catch return;
|
||||
self.handler.change(watch_path, EventType.renamed, .file) catch return;
|
||||
} else if (ev.fflags & (NOTE_WRITE | NOTE_EXTEND) != 0) {
|
||||
self.handler.change(watch_path.?, EventType.modified, .file) catch return;
|
||||
self.handler.change(watch_path, EventType.modified, .file) catch return;
|
||||
}
|
||||
} else {
|
||||
if (ev.fflags & NOTE_DELETE != 0) {
|
||||
self.handler.change(watch_path.?, EventType.deleted, .dir) catch return;
|
||||
self.handler.change(watch_path, EventType.deleted, .dir) catch return;
|
||||
} else if (ev.fflags & NOTE_RENAME != 0) {
|
||||
self.handler.change(watch_path.?, EventType.renamed, .dir) catch return;
|
||||
self.handler.change(watch_path, EventType.renamed, .dir) catch return;
|
||||
} else if (ev.fflags & NOTE_WRITE != 0) {
|
||||
self.scan_dir(allocator, watch_path.?) catch {};
|
||||
self.scan_dir(allocator, watch_path) catch {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue