From 94d51c59f0a872c57cb8daa1964ec033d77a79fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= Date: Thu, 8 Oct 2026 08:06:26 +0000 Subject: [PATCH] macsec: prevent AES-GCM nonce reuse after packet number wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After allocating the last valid packet number, MACsec wraps next_pn to zero and deactivates the transmit SA. This happens for both 32- and 64-bit types of packet numbers (at values 0xffffffff and 0xffffffffffffffff, respectively). TX packets that were still getting processed during deactivation keep being processed and then receive packet numbers. The first gets 0 and is correctly dropped, but next_pn is incremented to 1, which makes the next packet take number 1. It then does not get dropped and may induce a reuse of the AES-GCM nonce corresponding to value 1. This race affects TX packets that have already passed the SA activity check: packets that observe the inactive SA are correctly dropped. Reactivating the SA after PN wrap without a packet number can also cause nonce reuse. Keep next_pn at 0 after wrap, even if the SA is reactivated, so further packet number allocations return 0 and the corresponding packets are dropped. Fixes: c09440f7dcb3 ("macsec: introduce IEEE 802.1AE driver") Signed-off-by: Jérémy Jean Link: https://patch.msgid.link/20261008080625.360063-2-Jeremy.Jean@oss.cyber.gouv.fr Signed-off-by: Jakub Kicinski --- drivers/net/macsec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 409fd855235b..e435681447fb 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -487,6 +487,9 @@ static pn_t tx_sa_update_pn(struct macsec_tx_sa *tx_sa, spin_lock_bh(&tx_sa->lock); pn = tx_sa->next_pn_halves; + if (unlikely(pn.full64 == 0)) + goto out; + if (secy->xpn) tx_sa->next_pn++; else @@ -494,6 +497,8 @@ static pn_t tx_sa_update_pn(struct macsec_tx_sa *tx_sa, if (tx_sa->next_pn == 0) __macsec_pn_wrapped(secy, tx_sa); + +out: spin_unlock_bh(&tx_sa->lock); return pn;