mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-11 18:50:50 +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& 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.
|
// 1. Let callerContext be the running execution context.
|
||||||
// NOTE: No-op, kept by the VM in its execution context stack.
|
// 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));
|
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).
|
// 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.
|
// 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));
|
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
Add a link
Reference in a new issue