Improve checks for scalar _get_bits methods #1845

pull peterdettman wants to merge 1 commits into bitcoin-core:master from peterdettman:verify_get_bits changing 3 files +17 −6
  1. peterdettman commented at 10:55 AM on April 12, 2026: contributor

    Improves the VERIFY_CHECKs in all _scalar_get_bits_limb32 and _scalar_get_bits_var methods.

    The initial prompt was noticing that scalar_4x64_impl/secp256k1_scalar_get_bits_limb32 was not restricting to 32-bit limbs correctly. Then missing range checks for offset were added and all such checks rewritten to avoid overflow.

    With these changes, the _low and _4x64 impls of _get_bits_var can no longer forward to _get_bits_limb32, so those calls were inlined instead.

  2. Improve checks for scalar _get_bits methods 0cad3df503
  3. in src/scalar_4x64_impl.h:45 in 0cad3df503
      40 | @@ -41,20 +41,22 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig
      41 |  SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
      42 |      SECP256K1_SCALAR_VERIFY(a);
      43 |      VERIFY_CHECK(count > 0 && count <= 32);
      44 | -    VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6);
      45 | +    VERIFY_CHECK(offset <= 256 - count);
      46 | +    VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5);
    


    edilmedeiros commented at 10:54 PM on April 22, 2026:

    I'm convinced this will not break things and I like this more after fiddling with the bits.

    But feels like the original check combined with line 43 was already sufficient for checking we are not able to cross limbs when trying to collect bits from the scalar.

    One such case could be: offset = 31.

    With the original check: RHS: offset >> 6 = 0 LHS: (31 + count - 1) >> 6 => count < 34 so that RHS == LHS.

    But line 43 already guarantee that count <= 32. Similar for the other limbs.


    peterdettman commented at 9:16 AM on April 29, 2026:

    Despite this implementation being 4 x 64-bit limbs, I assume it must still conform to the generic scalar API, and this method (per name and documentation) limits the get to 32-bit limbs; the original check limits only to 64-bit limbs.

  4. in src/scalar_4x64_impl.h:53 in 0cad3df503
      50 |  
      51 |  SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
      52 |      SECP256K1_SCALAR_VERIFY(a);
      53 |      VERIFY_CHECK(count > 0 && count <= 32);
      54 | -    VERIFY_CHECK(offset + count <= 256);
      55 | +    VERIFY_CHECK(offset <= 256 - count);
    


    edilmedeiros commented at 10:59 PM on April 22, 2026:

    I wonder if those aren't the same check.

    I mean, offset + count could overflow while 256 - count can't because of the first check. Is that the intended fix?


    peterdettman commented at 9:16 AM on April 29, 2026:

    Yes, overflow.

  5. in src/scalar_4x64_impl.h:55 in 0cad3df503
      52 |      SECP256K1_SCALAR_VERIFY(a);
      53 |      VERIFY_CHECK(count > 0 && count <= 32);
      54 | -    VERIFY_CHECK(offset + count <= 256);
      55 | +    VERIFY_CHECK(offset <= 256 - count);
      56 |  
      57 |      if ((offset + count - 1) >> 6 == offset >> 6) {
    


    edilmedeiros commented at 11:08 PM on April 22, 2026:

    Here, we are checking if we are not crossing limbs when collecting bits. I think it's ok to inline the function in the following, but not so much if we are testing a different condition compared to the called function, which is what this PR proposes by changing line R45.

    Why not

        if ((offset + count - 1) >> 5 == offset >> 5) {
    

    as well?


    peterdettman commented at 9:18 AM on April 29, 2026:

    This check is implementation-specific and can take advantage of the 64-bit limbs, unlike the API-32-bit one.

  6. edilmedeiros commented at 11:33 PM on April 22, 2026: none

    Overall positive, this PR is indeed improving parameter checking.

    E.g. the invalid combination offset = 300; count = 10; would pass the original checks in secp256k1_scalar_get_bits_limb32.

    Do you think it's worth adding some unit tests to avoid future regression?

  7. real-or-random added the label assurance on Apr 23, 2026
  8. real-or-random added the label tweak/refactor on Apr 23, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/secp256k1. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-11 12:15 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me