From f69edb2e82add62500fd6e41dead82ae8cbc436a Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Thu, 8 Apr 2021 22:17:58 +0100 Subject: [PATCH] Handle corrupted generator or async generator prototype --- .../JavascriptAsyncGeneratorFunction.cpp | 10 +++++--- .../Library/JavascriptGeneratorFunction.cpp | 9 +++++--- lib/Runtime/Library/JavascriptLibrary.h | 3 +++ test/es6/generators-functionality.js | 23 +++++++++++++++++++ test/es7/async-generator-apis.js | 23 +++++++++++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp b/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp index 451c67465e1..65e9173aa85 100644 --- a/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp +++ b/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp @@ -50,10 +50,14 @@ Var JavascriptAsyncGeneratorFunction::EntryAsyncGeneratorFunctionImplementation( auto* asyncGeneratorFn = VarTo(function); auto* library = scriptContext->GetLibrary(); - auto* prototype = VarTo(JavascriptOperators::GetPropertyNoCache( - function, Js::PropertyIds::prototype, scriptContext)); + Var prototype = JavascriptOperators::GetPropertyNoCache(function, Js::PropertyIds::prototype, scriptContext); + + // fall back to the original prototype if we have an invalid prototype object + DynamicObject* protoObject = VarIs(prototype) ? + UnsafeVarTo(prototype) : library->GetAsyncGeneratorPrototype(); + auto* scriptFn = asyncGeneratorFn->GetGeneratorVirtualScriptFunction(); - auto* generator = library->CreateAsyncGenerator(args, scriptFn, prototype); + auto* generator = library->CreateAsyncGenerator(args, scriptFn, protoObject); // Run the generator to execute until the beginning of the body if (scriptFn->GetFunctionInfo()->GetGeneratorWithComplexParams()) diff --git a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp index fb7bcbb11b5..c44036ad3d1 100644 --- a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp +++ b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp @@ -112,13 +112,16 @@ using namespace Js; auto* library = scriptContext->GetLibrary(); auto* generatorFunction = VarTo(function); - DynamicObject* prototype = VarTo(JavascriptOperators::GetPropertyNoCache( - function, Js::PropertyIds::prototype, scriptContext)); + Var prototype = JavascriptOperators::GetPropertyNoCache(function, Js::PropertyIds::prototype, scriptContext); + + // fall back to the original prototype if we have an invalid prototype object + DynamicObject* protoObject = VarIs(prototype) ? + UnsafeVarTo(prototype) : library->GetGeneratorPrototype(); JavascriptGenerator* generator = library->CreateGenerator( args, generatorFunction->scriptFunction, - prototype); + protoObject); // Call a next on the generator to execute till the beginning of the body FunctionInfo* funcInfo = generatorFunction->scriptFunction->GetFunctionInfo(); diff --git a/lib/Runtime/Library/JavascriptLibrary.h b/lib/Runtime/Library/JavascriptLibrary.h index 35d1b0c57aa..c89b655653b 100644 --- a/lib/Runtime/Library/JavascriptLibrary.h +++ b/lib/Runtime/Library/JavascriptLibrary.h @@ -628,6 +628,9 @@ namespace Js DynamicObject* GetWebAssemblyLinkErrorPrototype() const { return webAssemblyLinkErrorPrototype; } DynamicObject* GetWebAssemblyLinkErrorConstructor() const { return webAssemblyLinkErrorConstructor; } + DynamicObject* GetAsyncGeneratorPrototype() const { return asyncGeneratorPrototype; } + DynamicObject* GetGeneratorPrototype() const { return generatorPrototype; } + DynamicObject* GetChakraLib() const { return chakraLibraryObject; } #if ENABLE_TTD diff --git a/test/es6/generators-functionality.js b/test/es6/generators-functionality.js index 68fc21857c7..45728b07df8 100644 --- a/test/es6/generators-functionality.js +++ b/test/es6/generators-functionality.js @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- @@ -56,6 +57,28 @@ function simpleThrowFunc() { var global = (function() { return this; }()); var tests = [ + { + name: "Generator function with overwritten prototype", + body: function () { + function* gf() {}; + var gfp = gf.prototype; + assert.strictEqual(gf().__proto__, gfp, "Generator function uses prototype."); + var obj = {}; + gf.prototype = obj; + assert.strictEqual(gf().__proto__, obj, "Generator function uses overwritten prototype."); + gf.prototype = 1; + assert.areEqual(gf().__proto__.toString(), gfp.toString(), "Generator function falls back to original prototype."); + if (gf().__proto__ === gfp) { assert.error("Original prototype should not be same object as gfp")} + var originalGfp = gf().__proto__; + assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype."); + gf.prototype = 0; + assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype."); + gf.prototype = "hi"; + assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype."); + delete gfp.prototype; + assert.strictEqual(gf().__proto__, originalGfp, "Generator function falls back to original prototype."); + } + }, { name: "Simple generator functions with no parameters or locals or captures", body: function () { diff --git a/test/es7/async-generator-apis.js b/test/es7/async-generator-apis.js index d8a53a13f96..207c2137fb1 100644 --- a/test/es7/async-generator-apis.js +++ b/test/es7/async-generator-apis.js @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- @@ -30,6 +31,28 @@ function testMethod(object, name, objectName, methodName = name) { } const tests = [ + { + name: "Async Generator function with overwritten prototype", + body: function () { + async function* agf() {}; + var gfp = agf.prototype; + assert.strictEqual(agf().__proto__, gfp, "Async Generator function uses prototype."); + var obj = {}; + agf.prototype = obj; + assert.strictEqual(agf().__proto__, obj, "Async Generator function uses overwritten prototype."); + agf.prototype = 1; + assert.areEqual(agf().__proto__.toString(), gfp.toString(), "Generator function falls back to original prototype."); + if (agf().__proto__ === gfp) { assert.error("Original prototype should not be same object as gfp")} + var originalGfp = agf().__proto__; + assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype."); + agf.prototype = 0; + assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype."); + agf.prototype = "hi"; + assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype."); + delete gfp.prototype; + assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype."); + } + }, { name: "AsyncGeneratorFunction is not exposed on the global object", body: function () {