From ba41f142edc815ae7bd5c4629d1d429497041a76 Mon Sep 17 00:00:00 2001 From: Christian Kugler Date: Sun, 11 Aug 2024 23:37:57 +0200 Subject: [PATCH 1/2] Add Config Option to Limit Number of Processes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To protect your system and ghostty from misbehaving programs that launch too many processes for the system to handle (e.g. like a fork bomb), this implements an option to limit the number of processes that can be started in a surface. A fork bomb for example or other misbehaving program would then only take down one surface and not the entire system. Side node: If I am right in issue #2084, this feature does not actually work on a per surface basis but on all surfaces. If this is the case, it could probably be fixed together. Chances are, that I am wrong though 😉 Further improvements that could be done: - unify way to set cgroup attributes - set sane default: 10% of system max? --- src/apprt/gtk/cgroup.zig | 8 ++++++++ src/config/Config.zig | 7 +++++++ src/os/cgroup.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/src/apprt/gtk/cgroup.zig b/src/apprt/gtk/cgroup.zig index b936d83a1..e767c9a23 100644 --- a/src/apprt/gtk/cgroup.zig +++ b/src/apprt/gtk/cgroup.zig @@ -71,6 +71,14 @@ pub fn init(app: *App) ![]const u8 { }); } + // Configure the "max" pids limit. This is a hard limit and cannot be + // exceeded. + if (app.config.@"linux-cgroup-processes-limit") |limit| { + try internal_os.cgroup.configureProcessesLimit(surfaces, .{ + .processes = limit, + }); + } + return transient; } diff --git a/src/config/Config.zig b/src/config/Config.zig index e5ee2bc22..7889e12a8 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1276,6 +1276,13 @@ keybind: Keybinds = .{}, /// pressure. @"linux-cgroup-memory-limit": ?u64 = null, +/// Number of processes limit for any individual terminal process (tab, split, +/// window, etc.). If this is unset then no limit will be set. +/// +/// Note that this sets the "pids.max" configuration for the process number +/// controller, which is a hard limit. +@"linux-cgroup-processes-limit": ?u64 = null, + /// If this is false, then any cgroup initialization (for linux-cgroup) /// will be allowed to fail and the failure is ignored. This is useful if /// you view cgroup isolation as a "nice to have" and not a critical resource diff --git a/src/os/cgroup.zig b/src/os/cgroup.zig index 5a4082b15..4b6b2335b 100644 --- a/src/os/cgroup.zig +++ b/src/os/cgroup.zig @@ -207,3 +207,30 @@ pub fn configureMemoryLimit(cgroup: []const u8, limit: MemoryLimit) !void { // Write our limit in bytes try file.writer().print("{}", .{size}); } + +pub const ProcessesLimit = union(enum) { + /// pids.max + processes: usize, +}; + +/// Configure the number of processes for the given cgroup. +pub fn configureProcessesLimit(cgroup: []const u8, limit: ProcessesLimit) !void { + assert(cgroup[0] == '/'); + + const filename, const size = switch (limit) { + .processes => |v| .{ "pids.max", v }, + }; + + // Open our file + var buf: [std.fs.max_path_bytes]u8 = undefined; + const path = try std.fmt.bufPrint( + &buf, + "/sys/fs/cgroup{s}/{s}", + .{ cgroup, filename }, + ); + const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); + defer file.close(); + + // Write our limit in bytes + try file.writer().print("{}", .{size}); +} From a158a1d45f3832e75c8701c0707c5bb6723e8363 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 11 Aug 2024 15:37:54 -0700 Subject: [PATCH 2/2] os: unify memory/processes cgroup limiting func --- src/apprt/gtk/cgroup.zig | 8 ++++---- src/os/cgroup.zig | 42 ++++++++-------------------------------- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/src/apprt/gtk/cgroup.zig b/src/apprt/gtk/cgroup.zig index e767c9a23..7eb72b9c6 100644 --- a/src/apprt/gtk/cgroup.zig +++ b/src/apprt/gtk/cgroup.zig @@ -66,16 +66,16 @@ pub fn init(app: *App) ![]const u8 { // can be monitored by things like systemd-oomd to kill if needed, // versus an instant hard kill. if (app.config.@"linux-cgroup-memory-limit") |limit| { - try internal_os.cgroup.configureMemoryLimit(surfaces, .{ - .high = limit, + try internal_os.cgroup.configureLimit(surfaces, .{ + .memory_high = limit, }); } // Configure the "max" pids limit. This is a hard limit and cannot be // exceeded. if (app.config.@"linux-cgroup-processes-limit") |limit| { - try internal_os.cgroup.configureProcessesLimit(surfaces, .{ - .processes = limit, + try internal_os.cgroup.configureLimit(surfaces, .{ + .pids_max = limit, }); } diff --git a/src/os/cgroup.zig b/src/os/cgroup.zig index 4b6b2335b..0a66c5987 100644 --- a/src/os/cgroup.zig +++ b/src/os/cgroup.zig @@ -180,45 +180,19 @@ pub fn configureControllers( try file.writer().writeAll(v); } -pub const MemoryLimit = union(enum) { - /// memory.high - high: usize, +pub const Limit = union(enum) { + memory_high: usize, + pids_max: usize, }; -/// Configure the memory limit for the given cgroup. Use the various -/// fields in MemoryLimit to configure a specific type of limit. -pub fn configureMemoryLimit(cgroup: []const u8, limit: MemoryLimit) !void { +/// Configure a limit for the given cgroup. Use the various +/// fields in Limit to configure a specific type of limit. +pub fn configureLimit(cgroup: []const u8, limit: Limit) !void { assert(cgroup[0] == '/'); const filename, const size = switch (limit) { - .high => |v| .{ "memory.high", v }, - }; - - // Open our file - var buf: [std.fs.max_path_bytes]u8 = undefined; - const path = try std.fmt.bufPrint( - &buf, - "/sys/fs/cgroup{s}/{s}", - .{ cgroup, filename }, - ); - const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); - defer file.close(); - - // Write our limit in bytes - try file.writer().print("{}", .{size}); -} - -pub const ProcessesLimit = union(enum) { - /// pids.max - processes: usize, -}; - -/// Configure the number of processes for the given cgroup. -pub fn configureProcessesLimit(cgroup: []const u8, limit: ProcessesLimit) !void { - assert(cgroup[0] == '/'); - - const filename, const size = switch (limit) { - .processes => |v| .{ "pids.max", v }, + .memory_high => |v| .{ "memory.high", v }, + .pids_max => |v| .{ "pids.max", v }, }; // Open our file