commit ddc0adda850b8f4451dbcdcdc3318b1bb32dcab7
parent 419819555d052d16174db8e74cf9dafef4819ae0
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 13:32:36 +0400
lib: bound payload_length by hop_payloads in process
payload_length was only checked against the 2600-byte decrypted
buffer, so a payload could extend into the zero-extension stream and
the forwarded packet got a short hop_payloads. It was also converted
to Int before any check, so lengths of 2^63 and above wrapped to
negative values and were accepted as an empty payload.
Compare the length in Word64 and require that the length prefix,
payload and next HMAC fit in 1300 bytes. Also reject lengths below
two, as the spec requires.
Diffstat:
1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/lib/Lightning/Protocol/BOLT4/Process.hs b/lib/Lightning/Protocol/BOLT4/Process.hs
@@ -144,6 +144,10 @@ xorBytes !a !b = BS.pack (BS.zipWith xor a b)
--
-- Parses BigSize length prefix, extracts payload bytes and
-- next HMAC.
+--
+-- The length prefix, payload and next HMAC must fit in the 1300-byte
+-- hop_payloads region, so that at least 1300 bytes remain for the
+-- next hop.
extractPayload
:: BS.ByteString
-> Either RejectReason
@@ -151,19 +155,22 @@ extractPayload
-- ^ (payload_bytes, next_hmac, remaining_hop_payloads)
extractPayload !decrypted = do
-- Parse length prefix
- (len, afterLen) <- case decodeBigSize decrypted of
+ (len64, afterLen) <- case decodeBigSize decrypted of
Nothing -> Left (InvalidPayload "invalid length prefix")
- Just (l, r) -> Right (fromIntegral l :: Int, r)
-
- -- Validate length
- if len > BS.length afterLen
- then Left (InvalidPayload "payload length exceeds buffer")
- else if len == 0
- then Left (InvalidPayload "zero-length payload")
+ Just r -> Right r
+
+ -- Validate length (in Word64, before converting to Int)
+ let !prefixLen = BS.length decrypted - BS.length afterLen
+ !maxLen = hopPayloadsSize - prefixLen - hmacSize
+ if len64 > fromIntegral maxLen
+ then Left (InvalidPayload "payload length exceeds hop_payloads")
+ else if len64 < 2
+ then Left (InvalidPayload "payload length below 2")
else pure ()
-- Extract payload bytes
- let !payloadBytes = BS.take len afterLen
+ let !len = fromIntegral len64 :: Int
+ !payloadBytes = BS.take len afterLen
!afterPayload = BS.drop len afterLen
-- Extract next HMAC (32 bytes)