Skip to content

fix: avoid int8_t truncation of -parbls before clamping#7454

Merged
PastaPastaPasta merged 2 commits into
dashpay:developfrom
thepastaclaw:fix/parbls-clamp
Jul 13, 2026
Merged

fix: avoid int8_t truncation of -parbls before clamping#7454
PastaPastaPasta merged 2 commits into
dashpay:developfrom
thepastaclaw:fix/parbls-clamp

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jul 13, 2026

Copy link
Copy Markdown

Issue being fixed or feature implemented

-parbls is parsed as an int64_t, but it was narrowed to int8_t before the value was adjusted and clamped. Values above 127, such as -parbls=200, could therefore wrap negative and take the "leave cores free" path instead of clamping to the configured maximum.

What was done?

Keep the BLS verification thread count in int64_t while applying the autodetection adjustment and clamp. Clamp the pre-subtraction value to [1, MAX_BLSCHECK_THREADS + 1], then subtract one and cast to int8_t. This preserves the intended [0, MAX_BLSCHECK_THREADS] result across the full accepted int64_t argument range without signed underflow.

How Has This Been Tested?

  • Built src/dashd and src/dash-cli on macOS arm64.
  • Started regtest nodes with -parbls=2147483681 and -parbls=9223372036854775807; both reported 33 BLS verification threads, the configured maximum.
  • Ran git diff --check.

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

The bls_threads lambda assigned args.GetIntArg("-parbls", ...) (int64_t)
into an int8_t before adding GetNumCores() and clamping. A large -parbls
value such as 200 truncates/wraps to a negative int8_t, wrongly taking the
"leave that many cores free" branch, and threads += GetNumCores() can
overflow int8_t. Use an int intermediate and cast to int8_t only after the
final clamp to [0, MAX_BLSCHECK_THREADS], matching the sibling -par handling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thepastaclaw

thepastaclaw commented Jul 13, 2026

Copy link
Copy Markdown
Author

✅ Final review complete — no blockers (commit 7e35ad8)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ce02b3e0f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/init.cpp Outdated
/*dash_dbs_in_memory=*/false,
/*bls_threads=*/[&args]() -> int8_t {
int8_t threads = args.GetIntArg("-parbls", llmq::DEFAULT_BLSCHECK_THREADS);
int threads = args.GetIntArg("-parbls", llmq::DEFAULT_BLSCHECK_THREADS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep -parbls in int64_t until it is clamped

When -parbls is set above INT_MAX, GetIntArg() has already parsed a valid int64_t, but this assignment narrows it to int before the clamp. On normal 32-bit-int builds a value such as -parbls=2147483681 becomes negative, takes the auto/leave-cores-free path, and ends up clamped near 0 instead of to MAX_BLSCHECK_THREADS, so the truncation bug still exists for larger inputs. Keeping threads as int64_t through the adjustment/clamp avoids that remaining wraparound before the final int8_t cast.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 7e35ad8. The value now stays in int64_t through adjustment and clamping; the clamp is applied before subtracting one so INT64_MIN cannot underflow either. A full dashd build passed, and regtest runs with -parbls=2147483681 and INT64_MAX both reported the configured maximum of 33 threads.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

LoadChainstate() now reads -parbls into an int, applies the existing autodetection adjustment, clamps (threads - 1) to the valid BLS thread range, and casts the result to int8_t.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Suggested reviewers: udjinm6, knst, kwvg, pastapastapasta, dashcoreautoguix

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main fix to -parbls handling and the truncation/clamping bug.
Description check ✅ Passed The description accurately explains the -parbls bug, the fix, and the testing performed.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final validation — Codex + Sonnet

At exact head 7e35ad8, the -parbls lambda correctly retains int64_t through GetIntArg, adjustment, and clamping, narrowing to int8_t only after the result is proven within [0,33]. The prior truncation blocker is fixed. Two suggestions remain: add regression test coverage for large -parbls values, and squash the two-commit stack since the second commit only widens an intermediate type that never independently shipped.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed)
  • Verifier: claude-sonnet-5 — final-verifier
  • Sonnet reviewers: claude-sonnet-5 — general (completed), claude-sonnet-5 — dash-core-commit-history (completed)

🟡 2 suggestion(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/init.cpp`:
- [SUGGESTION] src/init.cpp:2066-2077: Add regression coverage for large -parbls values
  No automated test exercises the boundary behavior this lambda implements — a repo-wide search for `parbls` only turns up src/init.cpp, src/llmq/options.h, and the man pages, no test file. A functional startup test (or a unit test that extracts the clamp logic) should cover at least `-parbls=200`, `-parbls=2147483648` (INT32_MAX+1), and `-parbls=9223372036854775807` (INT64_MAX), asserting the effective BLS thread count is clamped to 33.
- [SUGGESTION] src/init.cpp:2066-2077: Squash 7e35ad8b311 into 6ce02b3e0f0 — the follow-up only widens an intermediate that never shipped
  6ce02b3 changed the lambda's intermediate from int8_t to a plain int to fix the original truncation bug, but int (32-bit) is still narrower than the int64_t range GetIntArg can return for -parbls. 7e35ad8b311 immediately re-widens the identical code path to int64_t because int was still insufficient. Since neither intermediate state was ever released, this is one logical fix split into two commits — squash them into a single commit so the permanent history reflects only the correct final implementation.

Comment thread src/init.cpp
Comment on lines 2066 to 2077
/*bls_threads=*/[&args]() -> int8_t {
int8_t threads = args.GetIntArg("-parbls", llmq::DEFAULT_BLSCHECK_THREADS);
int64_t threads = args.GetIntArg("-parbls", llmq::DEFAULT_BLSCHECK_THREADS);
if (threads <= 0) {
// -parbls=0 means autodetect (number of cores - 1 validator threads)
// -parbls=-n means "leave n cores free" (number of cores - n - 1 validator threads)
threads += GetNumCores();
}
// Subtract 1 because the main thread counts towards the par threads
return std::clamp<int8_t>(threads - 1, 0, llmq::MAX_BLSCHECK_THREADS);
const int64_t adjusted_threads = std::clamp<int64_t>(
threads, 1, int64_t{llmq::MAX_BLSCHECK_THREADS} + 1) - 1;
return static_cast<int8_t>(adjusted_threads);
}(),

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🟡 Suggestion: Add regression coverage for large -parbls values

No automated test exercises the boundary behavior this lambda implements — a repo-wide search for parbls only turns up src/init.cpp, src/llmq/options.h, and the man pages, no test file. A functional startup test (or a unit test that extracts the clamp logic) should cover at least -parbls=200, -parbls=2147483648 (INT32_MAX+1), and -parbls=9223372036854775807 (INT64_MAX), asserting the effective BLS thread count is clamped to 33.

source: ['codex']

Comment thread src/init.cpp
Comment on lines 2066 to 2077
/*bls_threads=*/[&args]() -> int8_t {
int8_t threads = args.GetIntArg("-parbls", llmq::DEFAULT_BLSCHECK_THREADS);
int64_t threads = args.GetIntArg("-parbls", llmq::DEFAULT_BLSCHECK_THREADS);
if (threads <= 0) {
// -parbls=0 means autodetect (number of cores - 1 validator threads)
// -parbls=-n means "leave n cores free" (number of cores - n - 1 validator threads)
threads += GetNumCores();
}
// Subtract 1 because the main thread counts towards the par threads
return std::clamp<int8_t>(threads - 1, 0, llmq::MAX_BLSCHECK_THREADS);
const int64_t adjusted_threads = std::clamp<int64_t>(
threads, 1, int64_t{llmq::MAX_BLSCHECK_THREADS} + 1) - 1;
return static_cast<int8_t>(adjusted_threads);
}(),

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🟡 Suggestion: Squash 7e35ad8 into 6ce02b3 — the follow-up only widens an intermediate that never shipped

6ce02b3 changed the lambda's intermediate from int8_t to a plain int to fix the original truncation bug, but int (32-bit) is still narrower than the int64_t range GetIntArg can return for -parbls. 7e35ad8 immediately re-widens the identical code path to int64_t because int was still insufficient. Since neither intermediate state was ever released, this is one logical fix split into two commits — squash them into a single commit so the permanent history reflects only the correct final implementation.

source: ['claude', 'codex']

@PastaPastaPasta PastaPastaPasta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

utACK 7e35ad8

@PastaPastaPasta PastaPastaPasta merged commit 36589d4 into dashpay:develop Jul 13, 2026
49 checks passed
@UdjinM6 UdjinM6 added this to the 24 milestone Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants