Fix segfault in X509.set_serial_number for negative serials - #1534
Conversation
|
Thanks for the submission. I think we need to fix this, but another potential option would be to error on any negative integer. This has the advantage of being RFC 5280 compliant, but the disadvantage of changing pyOpenSSL's (wrong) behavior. @alex what do you think? |
|
I'm pro just rejecting it
All that is necessary for evil to succeed is for good people to do nothing.
…On Tue, Sep 1, 2026, 11:53 AM Paul Kehrer ***@***.***> wrote:
*reaperhulk* left a comment (pyca/pyopenssl#1534)
<#1534 (comment)>
Thanks for the submission. I think we need to fix this, but another
potential option would be to error on any negative integer. This has the
advantage of being RFC 5280 compliant, but the disadvantage of changing
pyOpenSSL's (wrong) behavior. @alex <https://github.com/alex> what do you
think?
—
Reply to this email directly, view it on GitHub
<#1534?email_source=notifications&email_token=AAAAGBAHMTWSXFOQHNARAV35M3WH7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNBZGY3DIOBWGM3KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-5496648636>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAAGBBKWTGWRVG67BNONGD5M3WH7AVCNFSNUABEKJSXA33TNF2G64TZHMYTKNZXHAYDKOJ3JFZXG5LFHM2TGMJTGUZTOMRRGOQXMAQ>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/AAAAGBB7GCK3HFAURAQFVET5M3WH7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNBZGY3DIOBWGM3KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y>
and Android
<https://github.com/notifications/mobile/android/AAAAGBA5OELTPEY5HWU44VT5M3WH7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNBZGY3DIOBWGM3KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>.
Download it today!
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
Happy to switch it to rejecting — RFC 5280 4.1.2.2 is unambiguous that the serial must be a positive integer, and it makes the failure loud instead of a segfault. One fact worth weighing before I push it, since it is the asymmetry the change creates. If that is acceptable — and I can see the argument that it should be, since the value was never conformant — say the word and I will replace the patch with a Either version is fine by me; it is your call on the compatibility trade-off. |
|
It's fine to not round trip. Let's just reject. |
|
Done — pushed as a follow-up commit rather than a rewrite so the change is visible in the diff.
The test asserts the rejection for -1, -255 and -(2**128 + 1), and also that a serial set earlier survives a rejected call. Removing just the new guard makes it fail with
|
| raise TypeError("serial must be an integer") | ||
|
|
||
| hex_serial = hex(serial)[2:] | ||
| # RFC 5280 section 4.1.2.2 requires the serial number to be a |
|
One outstanding request + need to rebase the changelog. |
hex(serial) renders negative values as "-0x...", so slicing off the first two characters left a stray "x" and BN_hex2bn failed to parse it. The failure went undetected because the guard compared BN_hex2bn's int return value against _ffi.NULL, which is always unequal, so the NULL BIGNUM was passed straight to BN_to_ASN1_INTEGER and dereferenced. Format the hex digits directly, which BN_hex2bn accepts with a leading "-", and check its return value against 0 as documented. X509.get_serial_number already round-trips negative values. Co-authored-by: Claude <noreply@anthropic.com>
RFC 5280 section 4.1.2.2 requires the serial number to be a positive integer, so set_serial_number now raises ValueError rather than encoding a value the standard does not permit. Moves the changelog entry to the backward-incompatible section: a negative serial previously crashed the interpreter, and callers relying on that value being stored will now see an exception. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M5uTyCU4WcNTsPvGrErDo
9ae7432 to
6eba158
Compare
|
Both done.
|
Problem
X509.set_serial_number()crashes the interpreter when passed a negative serial number:Reproduced on 26.4.0 from PyPI and on current
main.Cause
The hex string was built with
hex(serial)[2:]. For negative valueshex()emits-0x1, so the slice strips-0and leaves"x1", whichBN_hex2bncannot parse. The failure went undetected because the guard was:BN_hex2bnreturns anint(characters consumed,0on error), not a pointer, so comparing it against_ffi.NULLis always true. The still-NULLBIGNUMwas then handed toBN_to_ASN1_INTEGER, which dereferences it.Fix
Format the digits with
f"{serial:x}"—BN_hex2bnaccepts a leading-— and check the return value against0, as documented.get_serial_number()already round-trips negative values (BN_bn2hexemits-FF, whichint(..., 16)parses), so this restores set/get symmetry rather than introducing new behaviour.Test
test_negative_serial_numbercovers-1,-255and-(2**128 + 1). Reverting thef"{serial:x}"change makes it die with SIGSEGV; correcting only the!= 0guard makes it fail withErrorinstead. Both changed lines are executed by the test.A changelog entry is added under a new
26.5.0 (UNRELEASED)section.Noting it since it is the obvious first question: this API is already deprecated in favour of
cryptography'sCertificateBuilder. It is still shipped and callable, and the failure mode is an interpreter crash rather than an exception, which is why it seemed worth two lines. Happy to close if you would rather leave the deprecated surface alone.Prepared with assistance from Claude Code.