[PATCH net 1/2] ovpn: don't deref NULL key slot in ovpn_crypto_kill_key()
Junrui Luo via B4 Relay <[email protected]>
| Newsgroups | org.kernel.vger.stable,org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
From: Junrui Luo <[email protected]> ovpn_crypto_kill_key() is reached from ovpn_encrypt_post() when the packet ID space of the key in use has been exhausted and ovpn_pktid_xmit_next() returns -ERANGE. It locates the slot holding the given key ID by reading cs->slots[0] and cs->slots[1] and comparing ->key_id, but it dereferences both pointers without first checking them for NULL. An empty key slot is a perfectly normal state. Both ovpn_crypto_key_slot_delete() and ovpn_crypto_state_release() install NULL, and the ordinary rekeying sequence - install a new key in the secondary slot, swap, then delete the retired one - leaves primary_idx at 1 with slots[0] empty. In that state the very first comparison dereferences NULL. key_id sits at offset 0 of struct ovpn_crypto_key_slot, so this faults on a read of address 0. Every other slot accessor in this file already guards the pointer before touching it, e.g. ovpn_crypto_key_id_to_slot(): ks = rcu_dereference(cs->slots[idx]); if (ks && ks->key_id == key_id) Use the same NULL-safe form here. Fixes: 89d3c0e4612a ("ovpn: kill key and notify userspace in case of IV exhaustion") Reported-by: Yuhao Jiang <[email protected]> Assisted-by: Claude:claude-opus-5 Cc: [email protected] Signed-off-by: Junrui Luo <[email protected]> --- drivers/net/ovpn/crypto.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c index 90580e32052f..2c56fb180ed8 100644 --- a/drivers/net/ovpn/crypto.c +++ b/drivers/net/ovpn/crypto.c @@ -60,10 +60,12 @@ bool ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id) struct ovpn_crypto_key_slot *ks = NULL; spin_lock_bh(&cs->lock); - if (rcu_access_pointer(cs->slots[0])->key_id == key_id) { + if (rcu_access_pointer(cs->slots[0]) && + rcu_access_pointer(cs->slots[0])->key_id == key_id) { ks = rcu_replace_pointer(cs->slots[0], NULL, lockdep_is_held(&cs->lock)); - } else if (rcu_access_pointer(cs->slots[1])->key_id == key_id) { + } else if (rcu_access_pointer(cs->slots[1]) && + rcu_access_pointer(cs->slots[1])->key_id == key_id) { ks = rcu_replace_pointer(cs->slots[1], NULL, lockdep_is_held(&cs->lock)); } -- 2.51.2