diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index da4507f152c..ac61bfc35fa 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -439,6 +439,17 @@ ThrowCompletionOr> 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> ECMAScriptFunctionObject::internal_construct( this_argument = TRY(ordinary_create_from_constructor(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)); diff --git a/Libraries/LibJS/Tests/regress/proxied-constructor-leads-to-use-after-free.js b/Libraries/LibJS/Tests/regress/proxied-constructor-leads-to-use-after-free.js new file mode 100644 index 00000000000..b7523d1dd4d --- /dev/null +++ b/Libraries/LibJS/Tests/regress/proxied-constructor-leads-to-use-after-free.js @@ -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); +});