From 5fa1d8287d79616f55d77e3f9fc445c77f953464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 12 Jun 2023 11:02:22 -0700 Subject: [PATCH] Validate number of arguments passed to UIManagerBinding methods (#37783) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37783 We're not checking the number of arguments received before accessing the `arguments` array in JSI methods in `UIManagerBinding`, which could cause access to garbage values. This fixes that by adding a check in all methods to ensure the number of arguments received is at least the arity of the function. Changelog: [internal] Reviewed By: rshest Differential Revision: D46564580 fbshipit-source-id: 7012e0d6fb12c5024e4d6a4c0fb7a8c00263ccac --- .../renderer/uimanager/UIManagerBinding.cpp | 282 ++++++++++++------ 1 file changed, 190 insertions(+), 92 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 96cae3169e0..2e4db8f6e19 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -140,6 +140,19 @@ void UIManagerBinding::invalidate() const { uiManager_->setDelegate(nullptr); } +static void validateArgumentCount( + jsi::Runtime &runtime, + std::string const &methodName, + size_t expected, + size_t actual) { + if (actual < expected) { + throw jsi::JSError( + runtime, + methodName + " requires " + std::to_string(expected) + + " arguments, but only " + std::to_string(actual) + " were passed"); + } +} + jsi::Value UIManagerBinding::get( jsi::Runtime &runtime, jsi::PropNameID const &name) { @@ -177,15 +190,18 @@ jsi::Value UIManagerBinding::get( // Semantic: Creates a new node with given pieces. if (methodName == "createNode") { + auto paramCount = 5; return jsi::Function::createFromHostFunction( runtime, name, - 5, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto instanceHandle = instanceHandleFromValue(runtime, arguments[4], arguments[0]); if (!instanceHandle) { @@ -206,15 +222,18 @@ jsi::Value UIManagerBinding::get( // Semantic: Clones the node with *same* props and *same* children. if (methodName == "cloneNode") { + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -223,15 +242,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "setIsJSResponder") { + auto paramCount = 3; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + uiManager->setIsJSResponder( shadowNodeFromValue(runtime, arguments[0]), arguments[1].getBool(), @@ -242,15 +264,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "findNodeAtPoint") { + auto paramCount = 4; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) { + size_t count) { + validateArgumentCount(runtime, methodName, paramCount, count); + auto node = shadowNodeFromValue(runtime, arguments[0]); auto locationX = (Float)arguments[1].getNumber(); auto locationY = (Float)arguments[2].getNumber(); @@ -273,15 +298,18 @@ jsi::Value UIManagerBinding::get( // Semantic: Clones the node with *same* props and *empty* children. if (methodName == "cloneNodeWithNewChildren") { + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -292,15 +320,18 @@ jsi::Value UIManagerBinding::get( // Semantic: Clones the node with *given* props and *same* children. if (methodName == "cloneNodeWithNewProps") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, @@ -313,15 +344,18 @@ jsi::Value UIManagerBinding::get( // Semantic: Clones the node with *given* props and *empty* children. if (methodName == "cloneNodeWithNewChildrenAndProps") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto const &rawProps = RawProps(runtime, arguments[1]); return valueFromShadowNode( runtime, @@ -333,15 +367,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "appendChild") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + uiManager->appendChild( shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1])); @@ -353,11 +390,11 @@ jsi::Value UIManagerBinding::get( return jsi::Function::createFromHostFunction( runtime, name, - 1, + 0, [](jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const * /*arguments*/, - size_t /*count*/) noexcept -> jsi::Value { + size_t /*count*/) -> jsi::Value { auto shadowNodeList = std::make_shared( ShadowNode::ListOfShared({})); return valueFromShadowNodeList(runtime, shadowNodeList); @@ -365,14 +402,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "appendChildToSet") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [](jsi::Runtime &runtime, - jsi::Value const & /*thisValue*/, - jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + paramCount, + [methodName, paramCount]( + jsi::Runtime &runtime, + jsi::Value const & /*thisValue*/, + jsi::Value const *arguments, + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[0]); auto shadowNode = shadowNodeFromValue(runtime, arguments[1]); shadowNodeList->push_back(shadowNode); @@ -381,18 +422,21 @@ jsi::Value UIManagerBinding::get( } if (methodName == "completeRoot") { + auto paramCount = 2; std::weak_ptr weakUIManager = uiManager_; // Enhanced version of the method that uses `backgroundExecutor` and // captures a shared pointer to `UIManager`. return jsi::Function::createFromHostFunction( runtime, name, - 2, - [weakUIManager, uiManager]( + paramCount, + [weakUIManager, uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto runtimeSchedulerBinding = RuntimeSchedulerBinding::getBinding(runtime); auto surfaceId = surfaceIdFromValue(runtime, arguments[0]); @@ -449,15 +493,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "registerEventHandler") { + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [this]( + paramCount, + [this, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto eventHandler = arguments[0].getObject(runtime).getFunction(runtime); eventHandler_ = @@ -467,15 +514,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "getRelativeLayoutMetrics") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -491,15 +541,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "dispatchCommand") { + auto paramCount = 3; return jsi::Function::createFromHostFunction( runtime, name, - 3, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); if (shadowNode) { uiManager->dispatchCommand( @@ -512,15 +565,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "setNativeProps") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, const jsi::Value &, const jsi::Value *arguments, - size_t) -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + uiManager->setNativeProps_DEPRECATED( shadowNodeFromValue(runtime, arguments[0]), RawProps(runtime, arguments[1])); @@ -531,15 +587,18 @@ jsi::Value UIManagerBinding::get( // Legacy API if (methodName == "measureLayout") { + auto paramCount = 4; return jsi::Function::createFromHostFunction( runtime, name, - 4, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) { + size_t count) { + validateArgumentCount(runtime, methodName, paramCount, count); + auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1]).get(), @@ -567,15 +626,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "measure") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) { + size_t count) { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNode, nullptr, {/* .includeTransform = */ true}); @@ -609,15 +671,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "measureInWindow") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) { + size_t count) { + validateArgumentCount(runtime, methodName, paramCount, count); + auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -644,15 +709,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "sendAccessibilityEvent") { + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 2, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + uiManager->sendAccessibilityEvent( shadowNodeFromValue(runtime, arguments[0]), stringFromValue(runtime, arguments[1])); @@ -662,15 +730,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "configureNextLayoutAnimation") { + auto paramCount = 3; return jsi::Function::createFromHostFunction( runtime, name, - 3, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + uiManager->configureNextLayoutAnimation( runtime, // TODO: pass in JSI value instead of folly::dynamic to RawValue @@ -687,10 +758,10 @@ jsi::Value UIManagerBinding::get( name, 0, [this]( - jsi::Runtime &, - jsi::Value const &, - jsi::Value const *, - size_t) noexcept -> jsi::Value { + jsi::Runtime & /*runtime*/, + jsi::Value const & /*thisValue*/, + jsi::Value const * /*arguments*/, + size_t /*count*/) -> jsi::Value { return {serialize(currentEventPriority_)}; }); } @@ -704,15 +775,18 @@ jsi::Value UIManagerBinding::get( } if (methodName == "findShadowNodeByTag_DEPRECATED") { + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const &, jsi::Value const *arguments, - size_t) -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = uiManager->findShadowNodeByTag_DEPRECATED( tagFromValue(arguments[0])); @@ -743,15 +817,18 @@ jsi::Value UIManagerBinding::get( // /* width: */ number, // /* height: */ number // ] + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -781,15 +858,18 @@ jsi::Value UIManagerBinding::get( // Otherwise, it returns null. // getParent(shadowNode: ShadowNode): ?InstanceHandle + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto parentShadowNode = uiManager->getNewestParentOfShadowNode(*shadowNode); @@ -812,15 +892,18 @@ jsi::Value UIManagerBinding::get( // children. Otherwise, it returns an empty array. // getChildren(shadowNode: ShadowNode): Array + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto newestCloneOfShadowNode = @@ -845,15 +928,18 @@ jsi::Value UIManagerBinding::get( // the current revision of an active shadow tree. // isConnected(shadowNode: ShadowNode): boolean + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto newestCloneOfShadowNode = @@ -874,15 +960,18 @@ jsi::Value UIManagerBinding::get( // compareDocumentPosition(shadowNode: ShadowNode, otherShadowNode: // ShadowNode): number + auto paramCount = 2; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto otherShadowNode = shadowNodeFromValue(runtime, arguments[1]); @@ -908,15 +997,18 @@ jsi::Value UIManagerBinding::get( // not need any traversal. // getTextContent(shadowNode: ShadowNode): string + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto textContent = @@ -950,15 +1042,18 @@ jsi::Value UIManagerBinding::get( // /* top: */ number, // /* left: */ number, // ] + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto newestCloneOfShadowNode = @@ -1031,15 +1126,18 @@ jsi::Value UIManagerBinding::get( // /* scrollLeft: */ number, // /* scrollTop: */ number, // ] + auto paramCount = 1; return jsi::Function::createFromHostFunction( runtime, name, - 1, - [uiManager]( + paramCount, + [uiManager, methodName, paramCount]( jsi::Runtime &runtime, jsi::Value const & /*thisValue*/, jsi::Value const *arguments, - size_t /*count*/) noexcept -> jsi::Value { + size_t count) -> jsi::Value { + validateArgumentCount(runtime, methodName, paramCount, count); + auto shadowNode = shadowNodeFromValue(runtime, arguments[0]); auto newestCloneOfShadowNode =