mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
webapi, gc: Give FormData its own arena
Rather than relying on the frame_arena, use a distinct arena for FormData. This generally results in tighter memory usage, but more importantly it ensures that if FormData outlives the frame, we don't get a UAF. This can happen if the FormData is refernced in v8 and finalized late (e.g. after the frame would appear to still be needed). Also, in Frame.submitForm use the explicit acquireRef and releaseRef. This FormData can [in theory] be passed to JS, via the `formdata` event that we fire.
This commit is contained in:
@@ -4302,8 +4302,8 @@ pub fn submitForm(self: *Frame, submitter_: ?*Element, form_: ?*Element.Html.For
|
||||
// The submitter can be an input box (if enter was entered on the box)
|
||||
// I don't think this is technically correct, but FormData handles it ok
|
||||
const form_data = try FormData.init(form, submitter_, &self.js.execution);
|
||||
// FormData.init acquires file's references. So we must release them once done.
|
||||
defer form_data.deinit(self._page);
|
||||
form_data.acquireRef();
|
||||
defer form_data.releaseRef(self._page);
|
||||
|
||||
const arena = try self._session.getArena(.medium, "submitForm");
|
||||
errdefer self._session.releaseArena(arena);
|
||||
|
||||
@@ -72,14 +72,18 @@ pub const Entry = struct {
|
||||
};
|
||||
|
||||
pub fn init(form_: ?*Form, submitter: ?*Element, exec: *const Execution) !*FormData {
|
||||
const form = form_ orelse {
|
||||
return try exec._factory.create(FormData{
|
||||
._rc = .{},
|
||||
._arena = exec.arena,
|
||||
._entries = .empty,
|
||||
});
|
||||
const arena = try exec.getArena(.small, "FormData");
|
||||
errdefer exec.releaseArena(arena);
|
||||
|
||||
const form_data = try arena.create(FormData);
|
||||
form_data.* = .{
|
||||
._rc = .{},
|
||||
._arena = arena,
|
||||
._entries = .empty,
|
||||
};
|
||||
|
||||
const form = form_ orelse return form_data;
|
||||
|
||||
const frame = switch (exec.js.global) {
|
||||
.frame => |f| f,
|
||||
.worker => lp.assert(false, "FormData worker form", .{}),
|
||||
@@ -93,12 +97,10 @@ pub fn init(form_: ?*Form, submitter: ?*Element, exec: *const Execution) !*FormD
|
||||
form._constructing_entry_list = true;
|
||||
defer form._constructing_entry_list = false;
|
||||
|
||||
const form_data = try exec._factory.create(FormData{
|
||||
._rc = .{},
|
||||
._arena = exec.arena,
|
||||
._entries = try collectForm(frame.arena, form, submitter, frame),
|
||||
});
|
||||
form_data._entries = try collectForm(arena, form, submitter, frame);
|
||||
|
||||
// Hold a reference on each entry's File for the FormData's lifetime; released
|
||||
// in deinit.
|
||||
for (form_data._entries.items) |entry| {
|
||||
switch (entry.value) {
|
||||
.file => |file| file.acquireRef(),
|
||||
@@ -123,6 +125,8 @@ pub fn deinit(self: *FormData, page: *Page) void {
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
// Frees the entry list and this FormData itself; do not touch self afterwards.
|
||||
page.releaseArena(self._arena);
|
||||
}
|
||||
|
||||
pub fn releaseRef(self: *FormData, page: *Page) void {
|
||||
|
||||
Reference in New Issue
Block a user