Skip to content

[bug]: Expiry Field Overflow in Invoice Deserialization #9808

@erickcestari

Description

@erickcestari

Background

During differential fuzzing tests on different Lightning implementations using Bitcoinfuzz, we've identified an inconsistency in how LND handles the expiry field in BOLT11 invoices compared to other implementations like C-Lightning and LDK.

When deserializing an invoice with a large expiry value, LND produces a negative expiry value due to overflow, while other implementations handle the same value correctly. This is caused by LND's use of time.Duration (int64) for storing the expiry field and the additional conversion to nanoseconds (multiplying by 1,000,000,000), which significantly reduces the effective range compared to other implementations that use uint64.

Steps to reproduce

https://github.com/bitcoinfuzz/bitcoinfuzz/actions/runs/15021579593/job/42211806963#step:33:55

Invoice that demonstrates the issue:

lnbc1qqygh9qpp5s7zqqcqpjpqqlqqqqphqqqqqqqqcqpjqqqqqqqqqqqqqqqqqqqqqxqgsqqqqqqqdqqqqqqqqqqqqqpjpqqlqqqqqqqqqqqqqqcqpjqqqqqsqqqqqqqqqqqqqqqqqnqqnqqpqqnqqqqqqqqqqqqqqqqqlqqqqqqqqqqqqqqqqqqdfrf47

Results:

LND:
HASH=878400600190400f8000006e0000000001800640000000000000000000000000;AMOUNT=0;DESCRIPTION=;RECIPIENT=02f36b27dd1d95740c66c076fb53b54991a19a525d997be3a868ff6f542242b6bd;EXPIRY=-3646508323;TIMESTAMP=4480160;ROUTING_HINTS=0;MIN_CLTV=18
C-Lightning:
HASH=878400600190400f8000006e0000000001800640000000000000000000000000;AMOUNT=0;DESCRIPTION=;RECIPIENT=02f36b27dd1d95740c66c076fb53b54991a19a525d997be3a868ff6f542242b6bd;EXPIRY=549755813888;TIMESTAMP=4480160;ROUTING_HINTS=0;MIN_CLTV=18

Note the significant difference in the EXPIRY values: LND shows a negative value (-3646508323) while C-Lightning shows 549755813888.

The underlying issue is that LND stores the expiry as a time.Duration, which is an int64, and then multiplies the decoded expiry value by 1,000,000,000 to store it as nanoseconds. This conversion significantly reduces the effective range of valid expiry values that LND can handle without overflow.

While the BOLT11 specification doesn't specify a maximum for the expiration field (it could technically be up to 639 bytes long), most implementations assume it's at most a uint64.

Proposed Solution

Consider changing LND to use uint64 for expiry fields rather than time.Duration for better compatibility with other implementations.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions