From eb58fcaa0342a28632ae09f6f8c504861add06d2 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Mon, 11 May 2026 15:31:36 +0200 Subject: [PATCH] instantout/reservation: avoid uint32 underflow in expiry-bounds check InitFromClientRequestAction validates that the server-returned absolute expiry is within +/- expiryDelta of expectedExpiry = relativeExpiry + heightHint. Both sides were uint32, so when expectedExpiry < expiryDelta (low regtest heights, fresh deployments, anything with heightHint = 0 like the existing test fixtures) expectedExpiry - expiryDelta wrapped to ~2^32. The lower bound check then trivially admitted any reasonable response, and the client would accept e.g. Expiry = 0 from the server, immediately past the reservation's own deadline -- meaning the server can sweep via the expiry script path while the client still believes it owns the reservation slot. Promote the comparison to int64 so the arithmetic is sign-honest. This is the smallest patch that closes the underflow; a follow-up should also add an absolute floor (e.g. Expiry >= heightHint + minSafeExpiry) so the server cannot return a near-deadline reservation even within the delta. --- instantout/reservation/actions.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/instantout/reservation/actions.go b/instantout/reservation/actions.go index 8a805e4d..0fd09b6d 100644 --- a/instantout/reservation/actions.go +++ b/instantout/reservation/actions.go @@ -76,9 +76,12 @@ func (f *FSM) InitFromClientRequestAction(ctx context.Context, expectedExpiry := reservationRequest.relativeExpiry + reservationRequest.heightHint - // Check that the expiry is in the delta. - if requestResponse.Expiry < expectedExpiry-expiryDelta || - requestResponse.Expiry > expectedExpiry+expiryDelta { + // Check that the expiry is in the delta. Compare as int64 so the + // lower bound stays meaningful when expectedExpiry < expiryDelta + // (which would otherwise underflow the uint32 and accept any + // response below the upper bound). + if int64(requestResponse.Expiry) < int64(expectedExpiry)-int64(expiryDelta) || + int64(requestResponse.Expiry) > int64(expectedExpiry)+int64(expiryDelta) { return f.HandleError( fmt.Errorf("unexpected expiry height: %v, expected %v",