Skip to content

Commit b7de64a

Browse files
Check non-nil validator before accessing withdrawal credentials (#14705)
* Check non-nil validator before accessing withdrawal credentials * Updated changelog
1 parent 11aa51e commit b7de64a

File tree

4 files changed

+41
-2
lines changed

4 files changed

+41
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
119119
- P2P: Avoid infinite loop when looking for peers in small networks.
120120
- Fixed another rollback bug due to a context deadline.
121121
- Fix checkpoint sync bug on holesky. [pr](https://github.com/prysmaticlabs/prysm/pull/14689)
122+
- Added check to prevent nil pointer deference or out of bounds array access when validating the BLSToExecutionChange on an impossibly nil validator.
122123
- Fix proposer boost spec tests being flakey by adjusting start time from 3 to 2s into slot.
123124
- Fix segmentation fault in E2E when light-client feature flag is enabled. [PR](https://github.com/prysmaticlabs/prysm/pull/14699)
124125
- Fix `searchForPeers` infinite loop in small networks.
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package blocks
22

33
var ProcessBLSToExecutionChange = processBLSToExecutionChange
4-
4+
var ErrInvalidBLSPrefix = errInvalidBLSPrefix
55
var VerifyBlobCommitmentCount = verifyBlobCommitmentCount

beacon-chain/core/blocks/withdrawals.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,11 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si
100100
if err != nil {
101101
return nil, err
102102
}
103+
if val == nil {
104+
return nil, errors.Wrap(errInvalidWithdrawalCredentials, "validator is nil") // This should not be possible.
105+
}
103106
cred := val.WithdrawalCredentials
104-
if cred[0] != params.BeaconConfig().BLSWithdrawalPrefixByte {
107+
if len(cred) < 2 || cred[0] != params.BeaconConfig().BLSWithdrawalPrefixByte {
105108
return nil, errInvalidBLSPrefix
106109
}
107110

beacon-chain/core/blocks/withdrawals_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,42 @@ func TestProcessBLSToExecutionChange(t *testing.T) {
113113
require.NoError(t, err)
114114
require.DeepEqual(t, digest[:], val.WithdrawalCredentials)
115115
})
116+
t.Run("nil validator does not panic", func(t *testing.T) {
117+
priv, err := bls.RandKey()
118+
require.NoError(t, err)
119+
pubkey := priv.PublicKey().Marshal()
116120

121+
message := &ethpb.BLSToExecutionChange{
122+
ToExecutionAddress: []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13},
123+
ValidatorIndex: 0,
124+
FromBlsPubkey: pubkey,
125+
}
126+
127+
registry := []*ethpb.Validator{
128+
nil,
129+
}
130+
st, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{
131+
Validators: registry,
132+
Fork: &ethpb.Fork{
133+
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
134+
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
135+
},
136+
Slot: params.BeaconConfig().SlotsPerEpoch * 5,
137+
})
138+
require.NoError(t, err)
139+
140+
signature, err := signing.ComputeDomainAndSign(st, time.CurrentEpoch(st), message, params.BeaconConfig().DomainBLSToExecutionChange, priv)
141+
require.NoError(t, err)
142+
143+
signed := &ethpb.SignedBLSToExecutionChange{
144+
Message: message,
145+
Signature: signature,
146+
}
147+
_, err = blocks.ValidateBLSToExecutionChange(st, signed)
148+
// The state should return an empty validator, even when the validator object in the registry is
149+
// nil. This error should return when the withdrawal credentials are invalid or too short.
150+
require.ErrorIs(t, err, blocks.ErrInvalidBLSPrefix)
151+
})
117152
t.Run("non-existent validator", func(t *testing.T) {
118153
priv, err := bls.RandKey()
119154
require.NoError(t, err)

0 commit comments

Comments
 (0)