Compare commits

...

2 Commits

Author SHA1 Message Date
Preston Van Loon
226cde6969 Use FlooredSubSlot for safe arithmetic per review feedback
Address PR feedback: Use primitives.Slot.FlooredSubSlot() instead of manual
defensive checks in batch.blockRequest() and batch.blobRequest(). This
leverages the existing safe arithmetic methods on the Slot type.
2026-01-08 13:10:03 -06:00
Preston Van Loon
e0466737d1 Fix issue #16223: Prevent makeslice panic from invalid Count values
Add defensive checks to prevent panic from astronomically large Count values
that could result from unsigned integer underflow:

1. In batch.blockRequest() and batch.blobRequest(): Return Count=0 when
   end <= begin, preventing the underflow at the source.

2. In SendBeaconBlocksByRangeRequest(): Cap slice capacity to MaxRequestBlock
   before allocation to prevent panic even if upstream code produces invalid
   values.

These changes ensure robustness against bugs in batch calculation code that
could produce invalid slot ranges.
2026-01-07 12:08:58 -06:00
4 changed files with 98 additions and 3 deletions

View File

@@ -148,7 +148,7 @@ func (b batch) ensureParent(expected [32]byte) error {
func (b batch) blockRequest() *eth.BeaconBlocksByRangeRequest {
return &eth.BeaconBlocksByRangeRequest{
StartSlot: b.begin,
Count: uint64(b.end - b.begin),
Count: uint64(b.end.FlooredSubSlot(b.begin)),
Step: 1,
}
}
@@ -156,7 +156,7 @@ func (b batch) blockRequest() *eth.BeaconBlocksByRangeRequest {
func (b batch) blobRequest() *eth.BlobSidecarsByRangeRequest {
return &eth.BlobSidecarsByRangeRequest{
StartSlot: b.begin,
Count: uint64(b.end - b.begin),
Count: uint64(b.end.FlooredSubSlot(b.begin)),
}
}

View File

@@ -10,6 +10,93 @@ import (
"github.com/pkg/errors"
)
func TestBlockRequest(t *testing.T) {
cases := []struct {
name string
begin primitives.Slot
end primitives.Slot
expectedCount uint64
}{
{
name: "normal case",
begin: 100,
end: 200,
expectedCount: 100,
},
{
name: "end equals begin",
begin: 100,
end: 100,
expectedCount: 0,
},
{
name: "end less than begin (would underflow without check)",
begin: 200,
end: 100,
expectedCount: 0,
},
{
name: "zero values",
begin: 0,
end: 0,
expectedCount: 0,
},
{
name: "single slot",
begin: 0,
end: 1,
expectedCount: 1,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
b := batch{begin: tc.begin, end: tc.end}
req := b.blockRequest()
require.Equal(t, tc.expectedCount, req.Count)
require.Equal(t, tc.begin, req.StartSlot)
require.Equal(t, uint64(1), req.Step)
})
}
}
func TestBlobRequest(t *testing.T) {
cases := []struct {
name string
begin primitives.Slot
end primitives.Slot
expectedCount uint64
}{
{
name: "normal case",
begin: 100,
end: 200,
expectedCount: 100,
},
{
name: "end equals begin",
begin: 100,
end: 100,
expectedCount: 0,
},
{
name: "end less than begin (would underflow without check)",
begin: 200,
end: 100,
expectedCount: 0,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
b := batch{begin: tc.begin, end: tc.end}
req := b.blobRequest()
require.Equal(t, tc.expectedCount, req.Count)
require.Equal(t, tc.begin, req.StartSlot)
})
}
}
func TestSortBatchDesc(t *testing.T) {
orderIn := []primitives.Slot{100, 10000, 1}
orderOut := []primitives.Slot{10000, 100, 1}

View File

@@ -77,8 +77,13 @@ func SendBeaconBlocksByRangeRequest(
}
defer closeStream(stream, log)
// Cap the slice capacity to MaxRequestBlock to prevent panic from invalid Count values.
// This guards against upstream bugs that may produce astronomically large Count values
// (e.g., due to unsigned integer underflow).
sliceCap := min(req.Count, params.MaxRequestBlock(slots.ToEpoch(tor.CurrentSlot())))
// Augment block processing function, if non-nil block processor is provided.
blocks := make([]interfaces.ReadOnlySignedBeaconBlock, 0, req.Count)
blocks := make([]interfaces.ReadOnlySignedBeaconBlock, 0, sliceCap)
process := func(blk interfaces.ReadOnlySignedBeaconBlock) error {
blocks = append(blocks, blk)
if blockProcessor != nil {

View File

@@ -0,0 +1,3 @@
### Changed
- Added some defensive checks to prevent overflows in block batch requests.