mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-09 06:59:08 +02:00
OnDataPathRekeyed builds a chained offer, releases the lock, then sends it. A signal re-bootstrap can supersede that exchange in the gap, after which the stale offer still went out and the responder would reserve and commit an abandoned exchange, splitting the keys. Two guards, no revalidation of message contents: - Sender side: before putting the chain offer on the wire, re-check it is still the current exchange; drop it if a re-bootstrap already replaced it. - Responder side: a legitimate rotation offer acknowledges the exchange we are awaiting-ack on (ackConverged clears it on a match). Drop a chain offer whose ack did not match our current exchange — it is a rotation a newer signal round has superseded. A bootstrap (zero AckID) is authoritative and always wins. A supersede landing in the infinitesimal window after the sender check still leaks one offer, but the responder guard rejects it, so the new round stands. Neither guard retries, so there is no loop. Found in cubic review on #7098 (client/internal/pqkem/manager.go:402).