terminal: duplicate hyperlinks before replacement

startHyperlink accepts borrowed URI and ID slices. When those slices came from the current cursor hyperlink, startHyperlinkOnce ended and freed that hyperlink before duplicating the replacement, then dereferenced the released URI and segfaulted.

Duplicate the new hyperlink before ending the prior one. Aliased inputs remain valid through the copy, and allocation failure leaves the existing cursor hyperlink intact.
This commit is contained in:
Mitchell Hashimoto
2026-07-09 21:55:08 -07:00
parent dace6d1c6d
commit cfce1cd56c

View File

@@ -2460,9 +2460,6 @@ fn startHyperlinkOnce(
self: *Screen,
source: hyperlink.Hyperlink,
) (Allocator.Error || Page.InsertHyperlinkError)!void {
// End any prior hyperlink
self.endHyperlink();
// Allocate our new Hyperlink entry in non-page memory. This
// lets us quickly get access to URI, ID.
const link = try self.alloc.create(hyperlink.Hyperlink);
@@ -2470,6 +2467,10 @@ fn startHyperlinkOnce(
link.* = try source.dupe(self.alloc);
errdefer link.deinit(self.alloc);
// End any prior hyperlink only after duplicating the new value. The
// source slices are allowed to reference our current hyperlink.
self.endHyperlink();
// Insert the hyperlink into page memory
var page = self.cursor.page_pin.node.page();
const id: hyperlink.Id = try page.insertHyperlink(link.*);
@@ -10033,6 +10034,21 @@ test "Screen: hyperlink start/end" {
}
}
test "Screen: hyperlink accepts its current values" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, .{ .cols = 5, .rows = 5, .max_scrollback = 0 });
defer s.deinit();
try s.startHyperlink("http://example.com", "current");
const current = s.cursor.hyperlink.?;
try s.startHyperlink(current.uri, current.id.explicit);
try testing.expectEqualStrings("http://example.com", s.cursor.hyperlink.?.uri);
try testing.expectEqualStrings("current", s.cursor.hyperlink.?.id.explicit);
}
test "Screen: implicit hyperlink ID wraps" {
const testing = std.testing;
const alloc = testing.allocator;