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
This commit is contained in:
Rubén Norte
2023-06-12 11:02:22 -07:00
committed by Facebook GitHub Bot
parent 87c11bc3ad
commit 5fa1d8287d
@@ -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>(
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<UIManager> 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<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 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 =