Skip to content

chore: numeric audit 0#20491

Merged
ledwards2225 merged 14 commits intomerge-train/barretenbergfrom
lde/numeric-audit-0
Feb 18, 2026
Merged

chore: numeric audit 0#20491
ledwards2225 merged 14 commits intomerge-train/barretenbergfrom
lde/numeric-audit-0

Conversation

@ledwards2225
Copy link
Contributor

@ledwards2225 ledwards2225 commented Feb 13, 2026

Audit and cleanup up of barretenberg/numeric module. Largely adding some protections, tests and docs clarifications. Also some minor bug fixes:

  • operator bool() in uint128, uint256, uintx only checked the lowest limb/half; now checks all limbs
  • incorrect mask in uintx::slice() (full width instead of half-width when range == base_uint::length())

@ledwards2225 ledwards2225 changed the base branch from next to merge-train/barretenberg February 13, 2026 15:20
@ledwards2225 ledwards2225 marked this pull request as ready for review February 16, 2026 17:53
}
auto lo = static_cast<uint64_t>(u);
return static_cast<size_t>(__builtin_clzll(lo)) + 64;
if (lo != 0U) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

its technically undefined behavior to call __builtin_clzll on 0 so adding this extra protection (matches what we do for u256 below)

if (lower_bound == in) {
return in;
}
// Overflow check: lower_bound is the highest power of 2 <= in,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

probably overly cautious but avoids wrap in lower_bound * 2 if input is > numeric_limits/2

constexpr uint128_t& operator=(uint128_t&& other) = default;
constexpr ~uint128_t() = default;
explicit constexpr operator bool() const { return static_cast<bool>(data[0]); };
explicit constexpr operator bool() const
Copy link
Contributor Author

Choose a reason for hiding this comment

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

bug: was only checking lowest limb. wasn't ever showing up anywhere because we don't have instances where we directly treat a u128 as a bool

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe worth documenting that it converts any non-zero value to 1?

// NOTE: this instantiation is only used to maintain a 1024 barrett reduction test.
// The simpler route would have been to delete that test as this modulus is otherwise not used, but this was more
// conservative.
constexpr uint512_t TEST_MODULUS(uint256_t{ "0x04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b1" },
Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved to test suite


~uintx() = default;
constexpr explicit operator bool() const { return static_cast<bool>(lo); };
constexpr explicit operator bool() const { return static_cast<bool>(lo) || static_cast<bool>(hi); };
Copy link
Contributor Author

Choose a reason for hiding this comment

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

same bug as in u128

constexpr uintx slice(const uint64_t start, const uint64_t end) const
{
const uint64_t range = end - start;
const uintx mask = range == base_uint::length() ? -uintx(1) : (uintx(1) << range) - 1;
Copy link
Contributor Author

@ledwards2225 ledwards2225 Feb 16, 2026

Choose a reason for hiding this comment

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

bug: in the first branch of this conditional (range == base_uint::length()) the mask is twice as large as it should be. I.e. if range == base_uint::length() == 256, the mask is u512(-1) when it should be u256(-1). The second branch in the conditional behaves correctly for this case so I'm simply removing the conditional

@iakovenkos iakovenkos self-requested a review February 17, 2026 14:35
explicit constexpr operator bool() const { return static_cast<bool>(data[0]); };
explicit constexpr operator bool() const
{
return (data[0] != 0) || (data[1] != 0) || (data[2] != 0) || (data[3] != 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

also here

Copy link
Contributor

@iakovenkos iakovenkos left a comment

Choose a reason for hiding this comment

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

lgtm! the only question I have is re the bool cast. in circuit we def don't want to cast any non-zero value to 1, but maybe it's a standard convention for native arithmetic

@ledwards2225
Copy link
Contributor Author

lgtm! the only question I have is re the bool cast. in circuit we def don't want to cast any non-zero value to 1, but maybe it's a standard convention for native arithmetic

Yeah AFAIK "true iff !=0" is the universal behavior for integer types being cast to a bool so I think anything else would be confusing. Its marked explicit so it can only happen in truly boolean contexts so I think we're good

@ledwards2225 ledwards2225 merged commit 35af684 into merge-train/barretenberg Feb 18, 2026
13 checks passed
@ledwards2225 ledwards2225 deleted the lde/numeric-audit-0 branch February 18, 2026 14:52
github-merge-queue bot pushed a commit that referenced this pull request Feb 18, 2026
BEGIN_COMMIT_OVERRIDE
chore: chonk rec ver 0 (#20506)
fix: allocate non-gate selectors at trace-active size instead of dyadic
size (#20600)
chore: numeric audit 0 (#20491)
chore: prepare barretenberg-rs for crates.io publishing (#20496)
chore: add build_bench to ci-barretenberg-full (#20650)
chore: add component graphs for app-proving benchmarks (#20649)
END_COMMIT_OVERRIDE
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.

2 participants