mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-11 03:39:36 +00:00
terminal: wrap implicit hyperlink identifiers
Implicit OSC 8 hyperlinks incremented a u32 identifier with checked arithmetic even though the cursor contract allows the sequence to wrap. Reaching the maximum identifier therefore caused a runtime safety panic before the link could be installed. Use wrapping arithmetic for both the successful increment and the error rollback. The identifier now returns to zero at the boundary, while failed allocation attempts still restore the original value.
This commit is contained in:
@@ -2413,13 +2413,13 @@ pub fn startHyperlink(
|
||||
.id = if (id_) |id| .{
|
||||
.explicit = id,
|
||||
} else implicit: {
|
||||
defer self.cursor.hyperlink_implicit_id += 1;
|
||||
defer self.cursor.hyperlink_implicit_id +%= 1;
|
||||
break :implicit .{ .implicit = self.cursor.hyperlink_implicit_id };
|
||||
},
|
||||
};
|
||||
errdefer switch (link.id) {
|
||||
.explicit => {},
|
||||
.implicit => self.cursor.hyperlink_implicit_id -= 1,
|
||||
.implicit => self.cursor.hyperlink_implicit_id -%= 1,
|
||||
};
|
||||
|
||||
// Loop until we have enough page memory to add the hyperlink
|
||||
@@ -9996,6 +9996,43 @@ test "Screen: hyperlink start/end" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Screen: implicit hyperlink ID wraps" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try init(alloc, .{ .cols = 5, .rows = 5, .max_scrollback = 0 });
|
||||
defer s.deinit();
|
||||
|
||||
s.cursor.hyperlink_implicit_id = std.math.maxInt(size.OffsetInt);
|
||||
try s.startHyperlink("http://example.com", null);
|
||||
|
||||
try testing.expectEqual(@as(size.OffsetInt, 0), s.cursor.hyperlink_implicit_id);
|
||||
try testing.expectEqual(
|
||||
std.math.maxInt(size.OffsetInt),
|
||||
s.cursor.hyperlink.?.id.implicit,
|
||||
);
|
||||
|
||||
// A failed allocation must roll the wrapped counter back to its
|
||||
// original value as well.
|
||||
s.endHyperlink();
|
||||
s.cursor.hyperlink_implicit_id = std.math.maxInt(size.OffsetInt);
|
||||
var failing = testing.FailingAllocator.init(alloc, .{});
|
||||
failing.fail_index = failing.alloc_index;
|
||||
{
|
||||
const original_alloc = s.alloc;
|
||||
defer s.alloc = original_alloc;
|
||||
s.alloc = failing.allocator();
|
||||
try testing.expectError(
|
||||
error.OutOfMemory,
|
||||
s.startHyperlink("http://example.com", null),
|
||||
);
|
||||
}
|
||||
try testing.expectEqual(
|
||||
std.math.maxInt(size.OffsetInt),
|
||||
s.cursor.hyperlink_implicit_id,
|
||||
);
|
||||
}
|
||||
|
||||
test "Screen: hyperlink reuse" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
Reference in New Issue
Block a user