Commit 8d292297f2 for wordpress.org

commit 8d292297f22459138dbf900a2ac90e066ef85c6c
Author: westonruter <westonruter@git.wordpress.org>
Date:   Wed Sep 23 19:06:47 2026 +0000

    XML-RPC: Return faults for malformed multicall requests.

    Previously, a malformed `system.multicall` request could cause a fatal error instead of an XML-RPC fault. `IXR_Server::multiCall()` assumed each entry was a struct with a `methodName` and passed its `params` straight through to `IXR_Server::call()`, which called `count()` on them without checking for an array. On PHP 8 this resulted in a `TypeError` for non-array params.

    Now `IXR_Server::multiCall()` returns a `-32600` fault when its argument is not an array, and each entry that is not a struct with a string `methodName` gets its own `-32600` fault, while the remaining calls continue to run. Non-array params get a `-32602` fault, and missing params default to an empty array. Additionally, `IXR_Server::call()` now only unwraps a single parameter when the params are a list, so that a struct with a single member is passed to the method intact rather than as `null`.

    Types are also added for `IXR_Server::multiCall()` and `IXR_Error` to aid static analysis.

    Developed in https://github.com/WordPress/wordpress-develop/pull/13663.

    Props josephscott, westonruter.
    See #66168, #65817.
    Fixes #66160.

    Built from https://develop.svn.wordpress.org/trunk@63901


    git-svn-id: http://core.svn.wordpress.org/trunk@63070 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/IXR/class-IXR-error.php b/wp-includes/IXR/class-IXR-error.php
index 660f7d1091..f921c94cdf 100644
--- a/wp-includes/IXR/class-IXR-error.php
+++ b/wp-includes/IXR/class-IXR-error.php
@@ -8,12 +8,22 @@
  */
 class IXR_Error
 {
+    /**
+     * @var int
+     */
     var $code;
+
+    /**
+     * @var string
+     */
     var $message;

-	/**
-	 * PHP5 constructor.
-	 */
+    /**
+     * PHP5 constructor.
+     *
+     * @param int    $code    Fault code.
+     * @param string $message Fault string.
+     */
     function __construct( $code, $message )
     {
         $this->code = $code;
@@ -22,6 +32,9 @@ class IXR_Error

 	/**
 	 * PHP4 constructor.
+	 *
+	 * @param int    $code    Fault code.
+	 * @param string $message Fault string.
 	 */
 	public function IXR_Error( $code, $message ) {
 		self::__construct( $code, $message );
diff --git a/wp-includes/IXR/class-IXR-server.php b/wp-includes/IXR/class-IXR-server.php
index e329602910..e11f40fbe7 100644
--- a/wp-includes/IXR/class-IXR-server.php
+++ b/wp-includes/IXR/class-IXR-server.php
@@ -92,7 +92,7 @@ EOD;
         $method = $this->callbacks[$methodname];

         // Perform the callback and send the response
-        if (count($args) == 1) {
+        if (is_array($args) && count($args) == 1 && array_key_exists(0, $args)) {
             // If only one parameter just send that instead of the whole array
             $args = $args[0];
         }
@@ -194,14 +194,42 @@ EOD;
         return array_reverse(array_keys($this->callbacks));
     }

+    /**
+     * Handles a system.multicall request.
+     *
+     * @param array[] $methodcalls List of method call structs, each with a methodName and optional params.
+     * @return IXR_Error|array[] Error if the method calls are not an array, otherwise a list of results,
+     *                           each either a fault struct or a single-element array wrapping the result.
+     *
+     * @phpstan-param list<array{ methodName: string, params?: array<mixed> }> $methodcalls
+     * @phpstan-return IXR_Error|list<array{ faultCode: int, faultString: string }|array{ mixed }>
+     */
     function multiCall($methodcalls)
     {
         // See http://www.xmlrpc.com/discuss/msgReader$1208
+        if (!is_array($methodcalls)) {
+            return new IXR_Error(-32600, 'server error. invalid xml-rpc. system.multicall expects an array of method calls');
+        }
+
         $return = array();
         foreach ($methodcalls as $call) {
+            // Each call must be a struct naming the method to call.
+            if (!is_array($call) || !isset($call['methodName']) || !is_string($call['methodName'])) {
+                $return[] = array(
+                    'faultCode' => -32600,
+                    'faultString' => 'server error. invalid xml-rpc. Each multicall entry must be a struct with a string methodName'
+                );
+                continue;
+            }
+
             $method = $call['methodName'];
-            $params = $call['params'];
-            if ($method == 'system.multicall') {
+            $params = array();
+            if (isset($call['params'])) {
+                $params = $call['params'];
+            }
+            if (!is_array($params)) {
+                $result = new IXR_Error(-32602, 'server error. invalid method parameters. Each multicall entry params must be an array');
+            } else if ($method == 'system.multicall') {
                 $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
             } else {
                 $result = $this->call($method, $params);
diff --git a/wp-includes/version.php b/wp-includes/version.php
index a0fe0d7425..96b75ca898 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-alpha-63900';
+$wp_version = '7.2-alpha-63901';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.