What changed, and why it matters
This commit fixes a bug in Bitcoin Core's own testing tools. The test framework had a limit meant to avoid passing overly long command-line arguments to the bitcoin-cli program, but it was checking the size of the largest single argument instead of the total size of all arguments. The fix lowers the threshold and checks the total length, and adds tests to make sure large arguments still work by routing them through standard input instead. This is a test-only change and does not affect the Bitcoin Core software that users run.
No security action required. This is a test-framework reliability fix. Reviewers may verify the new threshold and sum-based accounting behave correctly for the functional test suite.
Security signals we found
Test-only change; no production code modified
Fixes incorrect command-line argument size accounting in test harness
Prevents spurious test failures / false positives when large RPC arguments are used in functional tests
Adds regression tests for large argument handling
Evidence from the diff
The patch modifies test/functional/test_framework/test_node.py. It renames CLI_MAX_ARG_SIZE (131071) to TEST_CLI_MAX_ARG_SIZE (1024) and changes the CLI argument-size guard from checking max(len(arg)) to checking sum(len(arg)). When the total exceeds 1024 bytes, arguments are passed via stdin instead of the command line. A regression test is added in test/functional/rpc_misc.py exercising echo with argument sizes 0, 1, 100, 131071, and 8 MiB. The change is purely in the functional-test framework, not in bitcoind/bitcoin-cli production code.
Changed components
test/functional/test_framework/test_node.pytest/functional/rpc_misc.pyInspect captured patch +20 / −4
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
index 3c7cce2f..f8384e5b 100755
--- a/test/functional/rpc_misc.py
+++ b/test/functional/rpc_misc.py
@@ -30,6 +30,13 @@ class RpcMiscTest(BitcoinTestFramework):
lambda: node.echo(arg9='trigger_internal_bug'),
)
+ self.log.info("test max arg size")
+ ARG_SZ_COMMON = 131071 # Common limit, used previously in the test framework, serves as a regression test
+ ARG_SZ_LARGE = 8 * 1024 * 1024 # A large size, which should be rare to hit in practice
+ for arg_sz in [0, 1, 100, ARG_SZ_COMMON, ARG_SZ_LARGE]:
+ arg_string = 'a' * arg_sz
+ assert_equal([arg_string, arg_string], node.echo(arg_string, arg_string))
+
self.log.info("test getmemoryinfo")
memory = node.getmemoryinfo()['locked']
assert_greater_than(memory['used'], 0)
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 3b081463..be9116de 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -46,7 +46,12 @@ BITCOIND_PROC_WAIT_TIMEOUT = 60
# The size of the blocks xor key
# from InitBlocksdirXorKey::xor_key.size()
NUM_XOR_BYTES = 8
-CLI_MAX_ARG_SIZE = 131071 # many systems have a 128kb limit per arg (MAX_ARG_STRLEN)
+# Many systems have a 128kB limit for a command size. Depending on the
+# platform, this limit may be larger or smaller. Moreover, when using the
+# 'bitcoin' command, it may internally insert more args, which must be
+# accounted for. There is no need to pick the largest possible value here
+# anyway and it should be fine to set it to 1kB in tests.
+TEST_CLI_MAX_ARG_SIZE = 1024
# The null blocks key (all 0s)
NULL_BLK_XOR_KEY = bytes([0] * NUM_XOR_BYTES)
@@ -928,10 +933,14 @@ class TestNodeCLI():
if clicommand is not None:
p_args += [clicommand]
p_args += pos_args + named_args
- max_arg_size = max(len(arg) for arg in p_args)
+
+ # TEST_CLI_MAX_ARG_SIZE is set low enough that checking the string
+ # length is enough and encoding to bytes is not needed before
+ # calculating the sum.
+ sum_arg_size = sum(len(arg) for arg in p_args)
stdin_data = self.input
- if max_arg_size > CLI_MAX_ARG_SIZE:
- self.log.debug(f"Cli: Command size {max_arg_size} too large, using stdin")
+ if sum_arg_size >= TEST_CLI_MAX_ARG_SIZE:
+ self.log.debug(f"Cli: Command size {sum_arg_size} too large, using stdin")
rpc_args = "\n".join([arg for arg in p_args[base_arg_pos:]])
if stdin_data is not None:
stdin_data += "\n" + rpc_args
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.