From 04fefe2c1c4d04d960490b55d239e0826b4f3e2c Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Wed, 29 Jul 2026 16:54:28 -0500 Subject: [PATCH] common: saturate marginal_feerate() instead of overflowing marginal_feerate() computed current_feerate * 1.1 as a double and converted the result back to u32. Since current_feerate is chosen by the peer in open_channel or update_fee, they could choose an absurdly high value that overflows u32 after the computation. UBSan reports: common/fee_states.c:179:10: runtime error: 4.72446e+09 is outside the range of representable values of type 'unsigned int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior common/fee_states.c:179:10 Do the arithmetic with u64 and saturate at UINT32_MAX to avoid the undefined behavior. Found by fuzzing with smite. Changelog-Fixed: JSON-RPC: `listpeerchannels` no longer derives `receivable_msat` from an overflowed fee estimate when the peer sets an absurd `feerate_per_kw`. --- common/fee_states.c | 8 ++++++-- common/test/run-marginal_feerate.c | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/common/fee_states.c b/common/fee_states.c index 1626ea824c0b..abbf56d920b9 100644 --- a/common/fee_states.c +++ b/common/fee_states.c @@ -175,8 +175,12 @@ u32 marginal_feerate(u32 current_feerate) /* This could happen in future if we celebrate sub-sat summer! */ if (current_feerate < minfeerate) current_feerate = minfeerate; - if (current_feerate > maxfeerate) - return current_feerate * 1.1; + if (current_feerate > maxfeerate) { + u64 marginal = ((u64)current_feerate * 11) / 10; + if (marginal > UINT32_MAX) + return UINT32_MAX; + return marginal; + } /* min gives 1, max gives 0.1 */ double proportion = 1.0 - ((double)current_feerate - minfeerate) / (maxfeerate - minfeerate) * 0.9; diff --git a/common/test/run-marginal_feerate.c b/common/test/run-marginal_feerate.c index e0d0ebd4ab72..f693980d75eb 100644 --- a/common/test/run-marginal_feerate.c +++ b/common/test/run-marginal_feerate.c @@ -127,5 +127,12 @@ int main(int argc, char *argv[]) u32 half = (45000 + 253)/2; assert(marginal_feerate(half) == (u32)(half * 1.55)); + /* These values straddle the case where adding 10% exceeds UINT32_MAX. + * No overflow should occur. */ + assert(marginal_feerate(3904515722) == UINT32_MAX - 1); + assert(marginal_feerate(3904515723) == UINT32_MAX); + assert(marginal_feerate(3904515724) == UINT32_MAX); + assert(marginal_feerate(UINT32_MAX) == UINT32_MAX); + common_shutdown(); }