Background
TheOwnable2Step account component was introduced in #2572 (v0.14.0). It exposes tansfer_ownership, accept_ownership, and renounce_ownership.
The gap
transfer_ownership(EMPTY_WORD) is documented as a way to cancel a pending nomination. However, if accept_ownership does not first assert that the stored nominated_owner is a non-zero word, a race or off-by-one mistake could allow ownership to be silently transferred to EMPTY_WORD:
- The account becomes permanently inaccessible because no signer can produce a valid signature for the zero key.
- Depending on the signature-verification path, the zero word might vacuously satisfy an empty auth map entry, which is a separate and potentially more severe concern.
Proposed fix
Add a guard at the start of accept_ownership that traps when no nomination is pending:
Assert there is an active nomination (nominated_owner != EMPTY_WORD)
exec.get_nominated_owner
push.0.0.0.0
exec.word::test_eq
assertz # error: no pending nomination
And add a test:
- Call
transfer_ownership(EMPTY_WORD) to cancel any existing nomination.
- Immediately call
accept_ownership -- it must trap.
- Verify the owner is unchanged.
Impact
Without this guard, any account using Ownable2Step can be permanently locked out if ownership is transferred to the zero address, whether by accident or by a malicious counterparty who times a replay of a stale signed transaction.