fix: reject out-of-range attestation committee index (#15855)

* reject committee index >= committees_per_slot in unaggregated attestation validation

* Create phrwlk_fix-attestation-committee-index-bound.md

* add a unit test

* fix test

* fixing test

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: james-prysm <james@prysmaticlabs.com>
This commit is contained in:
phrwlk
2025-10-15 19:02:08 +03:00
committed by GitHub
parent f67ca6ae5e
commit 5ced1125f2
4 changed files with 47 additions and 4 deletions

View File

@@ -94,9 +94,11 @@ func TestVerifyIndexInCommittee_ExistsInBeaconCommittee(t *testing.T) {
assert.ErrorContains(t, wanted, err)
assert.Equal(t, pubsub.ValidationReject, result)
att.Data.CommitteeIndex = 10000
// Test the edge case where committee index equals count (should be rejected)
// With 64 validators and minimal config, count = 2, so valid indices are 0 and 1
att.Data.CommitteeIndex = 2
_, _, result, err = service.validateCommitteeIndexAndCount(ctx, att, s)
require.ErrorContains(t, "committee index 10000 > 2", err)
require.ErrorContains(t, "committee index 2 >= 2", err)
assert.Equal(t, pubsub.ValidationReject, result)
}

View File

@@ -278,8 +278,8 @@ func (s *Service) validateCommitteeIndexAndCount(
} else {
ci = a.GetCommitteeIndex()
}
if uint64(ci) > count {
return 0, 0, pubsub.ValidationReject, fmt.Errorf("committee index %d > %d", ci, count)
if uint64(ci) >= count {
return 0, 0, pubsub.ValidationReject, fmt.Errorf("committee index %d >= %d", ci, count)
}
return ci, valCount, pubsub.ValidationAccept, nil
}

View File

@@ -611,3 +611,41 @@ func TestService_setSeenUnaggregatedAtt(t *testing.T) {
})
})
}
func Test_validateCommitteeIndexAndCount_Boundary(t *testing.T) {
ctx := t.Context()
// Create a minimal state with a known number of validators.
validators := uint64(64)
bs, _ := util.DeterministicGenesisState(t, validators)
require.NoError(t, bs.SetSlot(1))
s := &Service{}
// Build a minimal Phase0 attestation (unaggregated path).
att := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Slot: 1,
CommitteeIndex: 0,
},
}
// First call to obtain the active validator count used to derive committees per slot.
_, valCount, res, err := s.validateCommitteeIndexAndCount(ctx, att, bs)
require.NoError(t, err)
require.Equal(t, pubsub.ValidationAccept, res)
count := helpers.SlotCommitteeCount(valCount)
// committee_index == count - 1 should be accepted.
att.Data.CommitteeIndex = primitives.CommitteeIndex(count - 1)
_, _, res, err = s.validateCommitteeIndexAndCount(ctx, att, bs)
require.NoError(t, err)
require.Equal(t, pubsub.ValidationAccept, res)
// committee_index == count should be rejected (out of range).
att.Data.CommitteeIndex = primitives.CommitteeIndex(count)
_, _, res, err = s.validateCommitteeIndexAndCount(ctx, att, bs)
require.ErrorContains(t, "committee index", err)
require.Equal(t, pubsub.ValidationReject, res)
}

View File

@@ -0,0 +1,3 @@
### Fixed
- reject committee index >= committees_per_slot in unaggregated attestation validation