mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +00:00
Fix potential use-after-free with PerformanceObserver.
TL;DR - use page.arena instead of page.call_arena This probably comes from copying the implementation of MutationObserver and/or IntersectionObserver. But those dispatches are different in that they directly dispatch a slice (e.g. of MutationRecords) which gets mapped to a v8::Array when doing the callback. The MutationRecords exist on the heap, not in _pending_records, so the call_arena is fine. PerformanceObserver returns an Zig object, not a slice. Therefore it gets mapped to a v8::Object which references the Zig object. The state of that object, the _entries list, has to exist for the lifetime of that object, not the call_arena.
This commit is contained in:
@@ -112,3 +112,28 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id="microtask_access_to_records">
|
||||||
|
testing.async(async () => {
|
||||||
|
let savedRecords;
|
||||||
|
const promise = new Promise((resolve) => {
|
||||||
|
const element = document.createElement('div');
|
||||||
|
const observer = new MutationObserver((records) => {
|
||||||
|
// Save the records array itself
|
||||||
|
savedRecords = records;
|
||||||
|
resolve();
|
||||||
|
observer.disconnect();
|
||||||
|
});
|
||||||
|
observer.observe(element, { attributes: true });
|
||||||
|
element.setAttribute('test', 'value');
|
||||||
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
// Force arena reset by making a Zig call
|
||||||
|
document.getElementsByTagName('*');
|
||||||
|
|
||||||
|
testing.expectEqual(1, savedRecords.length);
|
||||||
|
testing.expectEqual('attributes', savedRecords[0].type);
|
||||||
|
testing.expectEqual('test', savedRecords[0].attributeName);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -36,3 +36,31 @@
|
|||||||
performance.mark("operationEnd", { startTime: 34.0 });
|
performance.mark("operationEnd", { startTime: 34.0 });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id="microtask_access_to_list">
|
||||||
|
{
|
||||||
|
|
||||||
|
let savedList;
|
||||||
|
const promise = new Promise((resolve) => {
|
||||||
|
const observer = new PerformanceObserver((list, observer) => {
|
||||||
|
savedList = list;
|
||||||
|
resolve();
|
||||||
|
observer.disconnect();
|
||||||
|
});
|
||||||
|
observer.observe({ type: "mark" });
|
||||||
|
performance.mark("testMark");
|
||||||
|
});
|
||||||
|
|
||||||
|
testing.async(async () => {
|
||||||
|
await promise;
|
||||||
|
// force a call_depth reset, which will clear the call_arena
|
||||||
|
document.getElementsByTagName('*');
|
||||||
|
|
||||||
|
const entries = savedList.getEntries();
|
||||||
|
testing.expectEqual(true, entries instanceof Array, {script_id: 'microtask_access_to_list'});
|
||||||
|
testing.expectEqual(1, entries.length);
|
||||||
|
testing.expectEqual("testMark", entries[0].name);
|
||||||
|
testing.expectEqual("mark", entries[0].entryType);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ pub fn disconnect(self: *PerformanceObserver, page: *Page) void {
|
|||||||
/// Returns the current list of PerformanceEntry objects
|
/// Returns the current list of PerformanceEntry objects
|
||||||
/// stored in the performance observer, emptying it out.
|
/// stored in the performance observer, emptying it out.
|
||||||
pub fn takeRecords(self: *PerformanceObserver, page: *Page) ![]*Performance.Entry {
|
pub fn takeRecords(self: *PerformanceObserver, page: *Page) ![]*Performance.Entry {
|
||||||
const records = try page.call_arena.dupe(*Performance.Entry, self._entries.items);
|
// Use page.arena instead of call_arena because this slice is wrapped in EntryList
|
||||||
|
// and may be accessed later.
|
||||||
|
const records = try page.arena.dupe(*Performance.Entry, self._entries.items);
|
||||||
self._entries.clearRetainingCapacity();
|
self._entries.clearRetainingCapacity();
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user