diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 13:52:14 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | a33fc226c32d97a053bafdb13d6ec3bf8b537f35 (patch) | |
| tree | 21e2df58d94bebe3effde8cb447ae7f64e9c5b9a /src/ninep/pty.zig | |
| parent | 0fb5881520884691c395ef1ce4b9d1d902434bd6 (diff) | |
| download | pardes-a33fc226c32d97a053bafdb13d6ec3bf8b537f35.tar.gz pardes-a33fc226c32d97a053bafdb13d6ec3bf8b537f35.zip | |
winsize refuses fewer than 2 rows, saying so, and its range reads 1x2 to 4096x4096 as the docs say
`winsize 20 1` was taken and quietly raised to 2 rows, so the write
succeeded with a size other than the one asked for. The range refusal
also said "1x1 to 4096x4096", although fs.md says at least 2 rows. One row
is now refused with "invalid winsize: at least 2 rows", and the range
text starts at 1x2. size already refused anything under 20x6 with a text
that names it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/pty.zig')
| -rw-r--r-- | src/ninep/pty.zig | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig index 390ae58d..2245255b 100644 --- a/src/ninep/pty.zig +++ b/src/ninep/pty.zig @@ -33,7 +33,17 @@ pub const pty_rows_min = 2; /// As the root ctl's `size` (4096 a side). const winsize_max = 4096; -const e_winsize_range = std.fmt.comptimePrint("invalid winsize: out of range, 1x1 to {d}x{d}", .{ winsize_max, winsize_max }); +const e_winsize_range = std.fmt.comptimePrint("invalid winsize: out of range, 1x{d} to {d}x{d}", .{ pty_rows_min, winsize_max, winsize_max }); +const e_winsize_rows = std.fmt.comptimePrint("invalid winsize: at least {d} rows", .{pty_rows_min}); + +/// `winsize C R` whose R is a size but fewer rows than a pty takes. +fn tooFewRows(line: []const u8) bool { + var words = std.mem.tokenizeAny(u8, line, " \t"); + if (!std.mem.eql(u8, words.next() orelse return false, "winsize")) return false; + _ = dimension(words.next() orelse return false) orelse return false; + const rows = dimension(words.next() orelse return false) orelse return false; + return words.next() == null and rows < pty_rows_min; +} /// A winsize whose numbers are numbers but not sizes: said as such. fn outOfRange(line: []const u8) bool { @@ -79,7 +89,7 @@ pub fn writeCtl(p: *Pardes, req: Req, id: usize) Reply { if (@import("../host_io.zig").Shell.refusal(bin[0..@min(bin.len, 200)], &why)) |refused| return tree.failText(req.tag, E.NOENT, std.fmt.bufPrint(&p.fs.ename, "exec: {s}", .{refused}) catch "exec: no such shell"); }; - if (!verb(p, id, line, apply)) return tree.failText(req.tag, E.INVAL, if (outOfRange(line)) e_winsize_range else e_bad_pty_ctl); + if (!verb(p, id, line, apply)) return tree.failText(req.tag, E.INVAL, if (tooFewRows(line)) e_winsize_rows else if (outOfRange(line)) e_winsize_range else e_bad_pty_ctl); } } return .{ .tag = req.tag, .written = @intCast(req.data.len) }; @@ -93,8 +103,10 @@ fn verb(p: *Pardes, id: usize, line: []const u8, apply: bool) bool { .winsize => { const cols = dimension(words.next() orelse return false) orelse return false; // Two rows at least, as a pane is laid out: a one-row pty - // loses its prompt's input mark and reads busy for ever. - const rows = @max(pty_rows_min, dimension(words.next() orelse return false) orelse return false); + // loses its prompt's input mark and reads busy for ever. Fewer + // is refused, never quietly raised. + const rows = dimension(words.next() orelse return false) orelse return false; + if (rows < pty_rows_min) return false; if (words.next() != null) return false; if (!apply) return true; p.emit(.{ .resize_pty = .{ .pane = @intCast(id), .cols = cols, .rows = rows } }); @@ -619,6 +631,13 @@ test "every pty/ctl verb, and every refusal" { try testing.expect(a.winsize == null); } + // One row is a size, but fewer than a pty takes: refused, and said so. + const one = wr(p, ctl, "winsize 80 1"); + try testing.expectEqual(E.INVAL, one.errno()); + try testing.expectEqualStrings("invalid winsize: at least 2 rows", one.reply.ename); + try testing.expect(one.winsize == null); + try testing.expectEqualStrings("invalid winsize: out of range, 1x2 to 4096x4096", e_winsize_range); + const spaced = wr(p, ctl, "\n winsize 90 20 \n\n"); try testing.expectEqual(Status.ok, spaced.reply.status); try testing.expectEqual(@as(u16, 90), spaced.winsize.?.cols); |
