mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 04:25:13 +00:00
LibJS: Fix UAF in ECMAScriptFunctionObject::internal_construct
Currently, we create `this_argument` with `ordinary_create_from_constructor`, then we use `arguments_list` to build the callee_context. The issue is we don't properly model the side-effects of `ordinary_create_from_constructor`, if `new_target` is a proxy object then when we `get` the prototype, arbitrary javascript can run. This javascript could perform a function call with enough arguments to reallocate the interpreters m_argument_values_buffer vector. This is dangerous and leads to a use-after-free, as our stack frame maintains a pointer to m_argument_values_buffer (`arguments_list`).
This commit is contained in:
parent
b8fa355a21
commit
f5a6704219
Notes:
github-actions[bot]
2025-03-19 09:31:57 +00:00
Author: https://github.com/ttrssreal Commit: https://github.com/LadybirdBrowser/ladybird/commit/f5a67042195 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3971 Reviewed-by: https://github.com/gmta ✅
2 changed files with 44 additions and 11 deletions
|
@ -439,6 +439,17 @@ ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(
|
|||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
auto callee_context = ExecutionContext::create();
|
||||
|
||||
// Non-standard
|
||||
callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters.size()));
|
||||
callee_context->arguments.append(arguments_list.data(), arguments_list.size());
|
||||
callee_context->passed_argument_count = arguments_list.size();
|
||||
if (arguments_list.size() < m_formal_parameters.size()) {
|
||||
for (size_t i = arguments_list.size(); i < m_formal_parameters.size(); ++i)
|
||||
callee_context->arguments.append(js_undefined());
|
||||
}
|
||||
|
||||
// 1. Let callerContext be the running execution context.
|
||||
// NOTE: No-op, kept by the VM in its execution context stack.
|
||||
|
||||
|
@ -453,17 +464,6 @@ ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(
|
|||
this_argument = TRY(ordinary_create_from_constructor<Object>(vm, new_target, &Intrinsics::object_prototype, ConstructWithPrototypeTag::Tag));
|
||||
}
|
||||
|
||||
auto callee_context = ExecutionContext::create();
|
||||
|
||||
// Non-standard
|
||||
callee_context->arguments.ensure_capacity(max(arguments_list.size(), m_formal_parameters.size()));
|
||||
callee_context->arguments.append(arguments_list.data(), arguments_list.size());
|
||||
callee_context->passed_argument_count = arguments_list.size();
|
||||
if (arguments_list.size() < m_formal_parameters.size()) {
|
||||
for (size_t i = arguments_list.size(); i < m_formal_parameters.size(); ++i)
|
||||
callee_context->arguments.append(js_undefined());
|
||||
}
|
||||
|
||||
// 4. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
|
||||
// NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check.
|
||||
TRY(prepare_for_ordinary_call(*callee_context, &new_target));
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
test("Proxied constructor should handle argument_buffer reallocation during prototype get()", () => {
|
||||
function foo() {}
|
||||
|
||||
let handler = {
|
||||
get() {
|
||||
// prettier-ignore
|
||||
foo(
|
||||
// make extra sure we trigger a reallocation
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41
|
||||
);
|
||||
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
function Construct() {
|
||||
// later use dangling pointer
|
||||
console.log(arguments);
|
||||
}
|
||||
|
||||
let ConstructProxy = new Proxy(Construct, handler);
|
||||
|
||||
new ConstructProxy(0x1);
|
||||
});
|
Loading…
Add table
Reference in a new issue