tests: Cover all rejection paths of ec_privkey_import_der() #1895

pull cavdarahmet wants to merge 1 commits into bitcoin-core:master from cavdarahmet:test-privkey-import-der-invalid changing 1 files +164 −0
  1. cavdarahmet commented at 8:00 PM on July 22, 2026: none

    ec_privkey_import_der() in contrib/lax_der_privatekey_parsing.c rejects malformed encodings through eight structural checks. None of them were reachable from the test suite: every return 0 in the function was uncovered, and only the successful round-trip path was exercised.

    Measured with gcc --coverage + gcov -b on ./tests 2:

    lines branches (taken at least once)
    master 82.69% 57.14%
    this PR 100% 100%

    What the test does:

    • Mutates every field of an encoding produced by ec_privkey_export_der(), for both the compressed (1-byte sequence length) and the uncompressed (2-byte) layout, and asserts the layout it mutates before mutating it.
    • Adds malformed encodings that are not truncations of a valid key. A truncated valid encoding is always caught by the sequence length check, so hand-crafted short encodings are the only way to reach the length checks behind it. This includes an indefinite-length sequence (30 80 ...) that is otherwise well-formed, and a sequence length that exceeds the input.
    • Checks that out32 is cleared whenever no key is extracted.
    • Checks the lax behaviour the parser exists for: a key shorter than 32 bytes is accepted and left-padded with zeroes.
    • Passes the input in a heap buffer of exactly the advertised length, so a read past the end is caught by ASan instead of silently succeeding on adjacent stack bytes. Four of the bounds checks are only detectable this way.

    I sanity-checked that the test is load-bearing by removing each check in the parser one at a time: 14 of 16 mutations are detected. The two that are not are the empty-input check (ASan does not instrument a zero-size allocation) and the if (privkey[1]) guard added in #879 (a zero-length memcpy from an out-of-bounds pointer is UB but invisible to every sanitizer).

    While writing this I also confirmed that the existing bounds checks form pointers such as privkey + len outside the caller's object before comparing them, which is UB in a strict reading of the standard and is not caught by ASan/UBSan (including -fsanitize=pointer-overflow). That is a separate concern and is addressed by @l0rinc; the {0x30, 0x82, 0xff, 0xff} vector here is a regression test for it. This PR does not change the parser.

    I am aware of #781. If the lax DER helpers are eventually removed these tests go with them; until then contrib/ is inside the coverage target described in CONTRIBUTING.md, and this file had the largest gap in it.

  2. tests: cover all rejection paths of ec_privkey_import_der
    Before this commit none of the rejection paths of ec_privkey_import_der
    were reachable from the test suite: every `return 0` in the function was
    uncovered, leaving contrib/lax_der_privatekey_parsing.c at 82.69% line
    and 57.14% branch coverage.
    
    Mutate every field of an exported key, for both the compressed (1-byte
    sequence length) and the uncompressed (2-byte) layout, and add malformed
    encodings that are not truncations of a valid key -- a truncated valid
    encoding is always caught by the sequence length check, so those are the
    only way to reach the length checks behind it. Also check that the output
    is cleared whenever no key is extracted, and that a key shorter than 32
    bytes is accepted and left-padded, which is the point of the lax parser.
    
    The input is passed in a heap buffer of exactly the advertised length so
    that a read past the end is caught by ASan rather than silently
    succeeding on adjacent stack bytes.
    
    This brings the file to 100% line and 100% branch coverage.
    6ce06c9a37
  3. cavdarahmet force-pushed on Aug 6, 2026
  4. cavdarahmet renamed this:
    tests: Add regression tests for ec_privkey_import_der() rejecting malformed input
    tests: Cover all rejection paths of ec_privkey_import_der()
    on Aug 6, 2026
  5. cavdarahmet commented at 4:52 AM on August 7, 2026: none

    Rebased on current master (post-0.8.0) and reworked the tests since the original push: both the compressed and the uncompressed layout are now mutated, rejected inputs are checked to clear the output, and every input is passed in a heap buffer of the exact advertised length so ASan can catch reads past the end. The file is now at 100% line and 100% branch coverage (gcov -b), see the updated description for details.

    This is test-only and touches no library code, so it is independent of the 0.8.0 release work. @real-or-random you merged #879, the last substantive change to this file, and mentioned keeping it in sync with Bitcoin Core's copy — would you mind taking a look? cc @theStack

  6. theStack added the label assurance on Aug 14, 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-08-23 23:15 UTC

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