[PATCH net 1/2] ovpn: don't deref NULL key slot in ovpn_crypto_kill_key()
Junrui Luo <[email protected]> Wed, 05 Aug 2026 21:29:36 +0800
| Newsgroups | org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel,org.kernel.vger.netdev,org.kernel.vger.stable |
|---|---|
| Message-ID | <[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