mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-06 09:01:53 +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: f5a6704219
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));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue