From 7a18df8aab1638622ab93fd3203930399c680544 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 09:46:55 -0300 Subject: A tag write refuses NUL and the other control characters; a Dump that fails says so A NUL written into a tag went into the dump, which the host then could not write, and Dump said nothing: a success that wrote nothing. Tag writes, the workspace's, a column's and a pane's, refuse control characters but tab and newline, DEL, C1 and bytes not UTF-8, EINVAL; and Dump reports any failure of its own instead of swallowing it. Co-Authored-By: Claude Opus 5.5 --- src/ninep/pane.zig | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/ninep/pane.zig') diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index d29c8b49..1148041a 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -458,8 +458,21 @@ fn writeBody(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { return .{ .tag = req.tag, .written = @intCast(take) }; } +/// Why `data` is no text a tag takes: a control character but a tab or a +/// newline, DEL, a C1 control, or bytes that are not UTF-8 (a NUL in a tag +/// made a Dump that wrote nothing). +pub fn tagFault(data: []const u8) ?[]const u8 { + for (data) |c| if ((c < ' ' and c != '\t' and c != '\n') or c == 0x7f) return "invalid tag text: a control character"; + if (!std.unicode.utf8ValidateSlice(data)) return "invalid tag text: not UTF-8"; + var i: usize = 0; + while (std.mem.indexOfScalarPos(u8, data, i, 0xC2)) |at| : (i = at + 1) + if (at + 1 < data.len and data[at + 1] <= 0x9F) return "invalid tag text: a control character"; + return null; +} + fn writeTag(req: Req, pane: *Pane) Reply { if (req.data.len == 0) return .{ .tag = req.tag, .written = 0 }; + if (tagFault(req.data)) |why| return tree.failText(req.tag, E.INVAL, why); // The tag's own text grows by what is written, newlines and all: a tag // is a text like any other (acme's tag file appends the same way). const pf = &pane.fs; -- cgit v1.3