Skip to content

Conversation

maflcko
Copy link
Member

@maflcko maflcko commented Aug 23, 2025

CLI_MAX_ARG_SIZE has many edge case issues:

  • It seems to be lower on some systems, but it is unknown how to reproduce locally: ci: limit max stack size to 512 KiB #33079 (comment)
  • MAX_ARG_STRLEN is a limit per arg, but we probably want "The maximum length of [all of] the arguments": See https://www.man7.org/linux/man-pages/man3/sysconf.3.html, section ARG_MAX - _SC_ARG_MAX.
  • It doesn't account for the additional args added by the bitcoin command later on:

    bitcoin/src/bitcoin.cpp

    Lines 85 to 92 in 73220fc

    args.emplace_back("bitcoin-cli");
    // Since "bitcoin rpc" is a new interface that doesn't need to be
    // backward compatible, enable -named by default so it is convenient
    // for callers to use a mix of named and unnamed parameters. Callers
    // can override this by specifying -nonamed, but should not need to
    // unless they are passing string values containing '=' characters
    // as unnamed parameters.
    args.emplace_back("-named");
  • It doesn't account for unicode encoding a string to bytes before taking its length.

The issues are mostly harmless edge cases, but it would be good to fix them. So do that here, by:

  • Replacing max() by sum(), to correctly take into account all args, not just the largest one.
  • Reduce CLI_MAX_ARG_SIZE, to account for the bitcoin command additional args.

Also, there is a test. The test can be called with ulimit to hopefully limit the max args size to the hard-coded value in the test framework. For reference:

$ ( ulimit -s 512 && python3 -c 'import os; print(os.sysconf("SC_ARG_MAX") )' ) 
131072

On top of this pull it should pass, ...

bash -c 'ulimit -s 512 && BITCOIN_CMD="bitcoin -M" ./bld-cmake/test/functional/rpc_misc.py --usecli -l DEBUG'

... and with the test_framework changes reverted, it should fail:

OSError: [Errno 7] Argument list too long: 'bitcoin'

Also, there is a commit to enable CI_LIMIT_STACK_SIZE=1 in the i686 task, because it should now be possible and no longer hit the hard-to-reproduce issue mentioned above.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 23, 2025

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33243.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK cedwies, enirox001
Concept ACK mzumsande

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

@maflcko maflcko changed the title 2508 test fix cli cmd size test: Fix CLI_MAX_ARG_SIZE issues Aug 23, 2025
@DrahtBot DrahtBot changed the title test: Fix CLI_MAX_ARG_SIZE issues test: Fix CLI_MAX_ARG_SIZE issues Aug 23, 2025
@DrahtBot DrahtBot added the Tests label Aug 23, 2025
@@ -30,6 +30,11 @@ def run_test(self):
lambda: node.echo(arg9='trigger_internal_bug'),
)

self.log.info("test max arg size")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also passes on master (MacOS environment with ARG_MAX = 1048576). It does not fail with the test_framework changes reverted.
However, the following test only passes on this PR, not on my master:

self.log.info("test many small args (sum-size trigger)")
tiny = "a" * 120000
args = [tiny] * 10
assert_equal(args, node.echo(*args))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also passes on master (MacOS environment with ARG_MAX = 1048576).

Thanks for adding a macos data point for reference. This further supports that the hard-coded value may be different on different platforms.

However, the following test only passes on this PR, not on my master:

Nice, thanks for adapting the test and checking the bugfix on macos.

@cedwies
Copy link

cedwies commented Aug 25, 2025

ACK fabe214

This PR fixes how the functional test framework estimates command-line argument size. Previously it checked only the longest argument, but the OS limit is on the total argv/env size, so it now sums all args. It also adds a functional test using very large echo args, which exercises the -stdin fallback. The test passes for me.

I appreciate lowering CLI_MAX_ARG_SIZE to 1024. It’s a very safe, conservative threshold that avoids platform-dependent ARG_MAX issues, and since larger payloads automatically go through -stdin anyway, I see no real downside. A nice fail-safe change IMO.

Copy link
Contributor

@enirox001 enirox001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK fa18c82 — The fix is a welcome improvement: summing arg sizes better reflects kernel ARG_MAX behavior. Good use of >= to match inclusive boundary semantics. Left a few non-blocking clarifier nits inline.

@maflcko maflcko force-pushed the 2508-test-fix-cli-cmd-size branch 2 times, most recently from fa25f97 to fac47f6 Compare August 26, 2025 11:13
@maflcko maflcko force-pushed the 2508-test-fix-cli-cmd-size branch from fac47f6 to fa96a4a Compare August 26, 2025 11:20
@cedwies
Copy link

cedwies commented Aug 26, 2025

ACK fa96a4a

@DrahtBot DrahtBot requested a review from enirox001 August 26, 2025 13:09
Copy link
Contributor

@enirox001 enirox001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK fa96a4a — thanks for addressing the nits and clarifying the test; LGTM.

Copy link
Contributor

@mzumsande mzumsande left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the relevant bottleneck for me (tested on Ubuntu and Debian) was not ARG_MAX, but MAX_ARG_STRLEN, which accounts for the maximum per arg, not the sum.
e.g. the folllowing passes

arg1=$(printf "%0*d" $((131072 - 1)) 0)
arg2=$(printf "%0*d" $((131072 - 1)) 0)
$(which printf) "%s %s" "$arg1" "$arg2" >/dev/null

whereas

arg3=$(printf "%0*d" $((131072)) 0)
$(which printf) "%s" "$arg3" >/dev/null

fails with bash: /usr/bin/printf: Argument list too long

But seeing how much this apparently differs between systems, Concept ACK to using a smaller limit.

@maflcko
Copy link
Member Author

maflcko commented Aug 27, 2025

the relevant bottleneck for me (tested on Ubuntu and Debian) was not ARG_MAX, but MAX_ARG_STRLEN, which accounts for the maximum per arg, not the sum.

Thanks for clarifying that this was intentionally picked and that MAX_ARG_STRLEN is a limit per arg, separate from the command size limit SC_ARG_MAX. SC_ARG_MAX is larger on many modern systems, but not all. Also, it reduces to MAX_ARG_STRLEN, or even lower on some systems. I've edited the pull description, to better reflect this.

@maflcko
Copy link
Member Author

maflcko commented Sep 4, 2025

(+GHA ci)

@maflcko maflcko closed this Sep 4, 2025
@maflcko maflcko reopened this Sep 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants