From 994d0c8bb3bcd186a8d355208391db024ce297c9 Mon Sep 17 00:00:00 2001 From: Himess Date: Sat, 11 Apr 2026 15:36:34 +0300 Subject: [PATCH 1/5] fix: use checked arithmetic for cumulative gas accounting in payload builder Use checked_add and saturating_add for cumulative gas tracking to prevent potential u64 overflow, consistent with the defensive arithmetic pattern applied to reward_beneficiary in #21. --- crates/execution-payload/src/payload.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index b5b8775..1058789 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -575,7 +575,7 @@ where } // ensure we still have capacity for this transaction - if cumulative_gas_used.saturating_add(pool_tx.gas_limit()) > block_gas_limit { + if cumulative_gas_used.checked_add(pool_tx.gas_limit()).is_none_or(|total| total > block_gas_limit) { // we can't fit this transaction into the block, so we need to mark it as invalid // which also removes all dependent transaction from the iterator before we can // continue From 1fdc958dba39dc22a4b3a2971e1906bfa3a30df1 Mon Sep 17 00:00:00 2001 From: Himess Date: Mon, 13 Apr 2026 17:11:22 +0300 Subject: [PATCH 2/5] fix: replace saturating_add with checked_add + error propagation per review Per reviewer feedback, saturating_add silently clamps at u64::MAX which would corrupt cumulative_gas_used rather than fail the block build cleanly. Switch to checked_add with PayloadBuilderError propagation, matching the defensive pattern used at the capacity check on line 579. --- crates/execution-payload/src/payload.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index 1058789..18e4203 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -662,7 +662,13 @@ where { total_fees += U256::from(miner_fee) * U256::from(gas_used); } - cumulative_gas_used = cumulative_gas_used.saturating_add(gas_used); + cumulative_gas_used = cumulative_gas_used + .checked_add(gas_used) + .ok_or_else(|| { + PayloadBuilderError::other(eyre::eyre!( + "cumulative_gas_used overflow: {cumulative_gas_used} + {gas_used}" + )) + })?; } PayloadBuildMetrics::record_stage_tx_execution(loop_started.elapsed()); From 199ce62cc6a0ab9a3a00d17a3eae47857bd13764 Mon Sep 17 00:00:00 2001 From: Himess <95512809+Himess@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:04:49 +0300 Subject: [PATCH 3/5] Update crates/execution-payload/src/payload.rs Co-authored-by: Milap Sheth --- crates/execution-payload/src/payload.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index 18e4203..074cea6 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -575,7 +575,7 @@ where } // ensure we still have capacity for this transaction - if cumulative_gas_used.checked_add(pool_tx.gas_limit()).is_none_or(|total| total > block_gas_limit) { + if block_gas_limit < cumulative_gas_used.checked_add(pool_tx.gas_limit()).expect("total gas shouldn't overflow") { // we can't fit this transaction into the block, so we need to mark it as invalid // which also removes all dependent transaction from the iterator before we can // continue From 90b42d5eef5c8f53b5556e02cfd606f15d9823c1 Mon Sep 17 00:00:00 2001 From: Himess <95512809+Himess@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:04:57 +0300 Subject: [PATCH 4/5] Update crates/execution-payload/src/payload.rs Co-authored-by: Milap Sheth --- crates/execution-payload/src/payload.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index 074cea6..0f4d82e 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -662,13 +662,7 @@ where { total_fees += U256::from(miner_fee) * U256::from(gas_used); } - cumulative_gas_used = cumulative_gas_used - .checked_add(gas_used) - .ok_or_else(|| { - PayloadBuilderError::other(eyre::eyre!( - "cumulative_gas_used overflow: {cumulative_gas_used} + {gas_used}" - )) - })?; + cumulative_gas_used = cumulative_gas_used.checked_add(gas_used).expect("total gas shouldn't overflow"); } PayloadBuildMetrics::record_stage_tx_execution(loop_started.elapsed()); From 6ae01e4e69f4047e1e94d27dc387d2af6ec2d9e1 Mon Sep 17 00:00:00 2001 From: Himess Date: Thu, 30 Apr 2026 00:04:17 +0300 Subject: [PATCH 5/5] fix: cargo fmt --- crates/execution-payload/src/payload.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index 0f4d82e..b276575 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -575,7 +575,11 @@ where } // ensure we still have capacity for this transaction - if block_gas_limit < cumulative_gas_used.checked_add(pool_tx.gas_limit()).expect("total gas shouldn't overflow") { + if block_gas_limit + < cumulative_gas_used + .checked_add(pool_tx.gas_limit()) + .expect("total gas shouldn't overflow") + { // we can't fit this transaction into the block, so we need to mark it as invalid // which also removes all dependent transaction from the iterator before we can // continue @@ -662,7 +666,9 @@ where { total_fees += U256::from(miner_fee) * U256::from(gas_used); } - cumulative_gas_used = cumulative_gas_used.checked_add(gas_used).expect("total gas shouldn't overflow"); + cumulative_gas_used = cumulative_gas_used + .checked_add(gas_used) + .expect("total gas shouldn't overflow"); } PayloadBuildMetrics::record_stage_tx_execution(loop_started.elapsed());