From: Rafael Passos <rafael@rcpassos.me>
To: andrew+netdev@lunn.ch, tytso@mit.edu
Cc: rafael@rcpassos.me, Jason@zx2c4.com, andrew@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
pabeni@redhat.com,
syzbot+9ca7674fa7521a3f1bc2@syzkaller.appspotmail.com,
syzkaller-bugs@googlegroups.com, wireguard@lists.zx2c4.com
Subject: [PATCH v2] Wireguard: Fix data-race in rx/tx counter
Date: Mon, 29 Jun 2026 09:59:13 -0300 [thread overview]
Message-ID: <20260629125913.2354450-1-rafael@rcpassos.me> (raw)
In-Reply-To: <DJL786MPR9HO.BWHI3U37PDVM@rcpassos.me>
Fix data-race in {rx/tx}_bytes counter for wireguard connection.
These values were incremented inside a read_lock_bh block, without
exclusive write protection.
Using per-cpu counters guarantees consistency, and move overhead only to
the read part: the least frequent operation.
Signed-off-by: Rafael Passos <rafael@rcpassos.me>
---
As we discussed in the thread, keeping this counters accurate might not
be worth this extra memory cost of per-cpu counter.
It was great reading and learning about it. I think this would have been
a good patch :)
When looking around drivers/net, I found a few u64_stats uses without
per-cpu. I will do some digging to understand the reasoning behind it,
compared to the original "bare u64 counters" in wireguard.
Thanks,
Rafael Passos
drivers/net/wireguard/netlink.c | 22 ++++++++++++++++++++--
drivers/net/wireguard/peer.c | 7 ++++++-
drivers/net/wireguard/peer.h | 7 ++++++-
drivers/net/wireguard/receive.c | 9 ++++++++-
drivers/net/wireguard/socket.c | 9 +++++++--
5 files changed, 47 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c
index 1da7e98d0d50..f5978a8c2ca3 100644
--- a/drivers/net/wireguard/netlink.c
+++ b/drivers/net/wireguard/netlink.c
@@ -105,13 +105,31 @@ get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx)
if (fail)
goto err;
+ // read per-cpu counters for peer rx/tx_bytes
+ u64 total_rx_bytes = 0, total_tx_bytes = 0;
+ u64 rx_bytes = 0, tx_bytes = 0;
+ struct wg_peer_stats *pcpu_ptr;
+ unsigned int cpu, start;
+
+ for_each_possible_cpu(cpu) {
+ pcpu_ptr = per_cpu_ptr(peer->pcpu_stats, cpu);
+ do {
+ start = u64_stats_fetch_begin(&pcpu_ptr->syncp);
+ rx_bytes = u64_stats_read(&pcpu_ptr->rx_bytes);
+ tx_bytes = u64_stats_read(&pcpu_ptr->tx_bytes);
+ } while (u64_stats_fetch_retry(&pcpu_ptr->syncp, start));
+
+ total_rx_bytes += rx_bytes;
+ total_tx_bytes += tx_bytes;
+ }
+
if (nla_put(skb, WGPEER_A_LAST_HANDSHAKE_TIME,
sizeof(last_handshake), &last_handshake) ||
nla_put_u16(skb, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
peer->persistent_keepalive_interval) ||
- nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, peer->tx_bytes,
+ nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, total_tx_bytes,
WGPEER_A_UNSPEC) ||
- nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, peer->rx_bytes,
+ nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, total_rx_bytes,
WGPEER_A_UNSPEC) ||
nla_put_u32(skb, WGPEER_A_PROTOCOL_VERSION, 1))
goto err;
diff --git a/drivers/net/wireguard/peer.c b/drivers/net/wireguard/peer.c
index 1cb502a932e0..a37aa31f132a 100644
--- a/drivers/net/wireguard/peer.c
+++ b/drivers/net/wireguard/peer.c
@@ -36,6 +36,10 @@ struct wg_peer *wg_peer_create(struct wg_device *wg,
if (unlikely(dst_cache_init(&peer->endpoint_cache, GFP_KERNEL)))
goto err;
+ peer->pcpu_stats = alloc_percpu(struct wg_peer_stats);
+ if (unlikely(!peer->pcpu_stats))
+ goto err;
+
peer->device = wg;
wg_noise_handshake_init(&peer->handshake, &wg->static_identity,
public_key, preshared_key, peer);
@@ -189,7 +193,8 @@ static void rcu_release(struct rcu_head *rcu)
dst_cache_destroy(&peer->endpoint_cache);
WARN_ON(wg_prev_queue_peek(&peer->tx_queue) || wg_prev_queue_peek(&peer->rx_queue));
-
+
+ free_percpu(peer->pcpu_stats);
/* The final zeroing takes care of clearing any remaining handshake key
* material and other potentially sensitive information.
*/
diff --git a/drivers/net/wireguard/peer.h b/drivers/net/wireguard/peer.h
index 718fb42bdac7..e01781724aa1 100644
--- a/drivers/net/wireguard/peer.h
+++ b/drivers/net/wireguard/peer.h
@@ -34,6 +34,11 @@ struct endpoint {
};
};
+struct wg_peer_stats {
+ u64_stats_t rx_bytes, tx_bytes;
+ struct u64_stats_sync syncp;
+};
+
struct wg_peer {
struct wg_device *device;
struct prev_queue tx_queue, rx_queue;
@@ -49,7 +54,7 @@ struct wg_peer {
struct work_struct transmit_handshake_work, clear_peer_work, transmit_packet_work;
struct cookie latest_cookie;
struct hlist_node pubkey_hash;
- u64 rx_bytes, tx_bytes;
+ struct wg_peer_stats __percpu *pcpu_stats;
struct timer_list timer_retransmit_handshake, timer_send_keepalive;
struct timer_list timer_new_handshake, timer_zero_key_material;
struct timer_list timer_persistent_keepalive;
diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c
index eb8851113654..d799370122b5 100644
--- a/drivers/net/wireguard/receive.c
+++ b/drivers/net/wireguard/receive.c
@@ -19,8 +19,15 @@
/* Must be called with bh disabled. */
static void update_rx_stats(struct wg_peer *peer, size_t len)
{
+ struct wg_peer_stats *pcpu_ptr;
+
dev_sw_netstats_rx_add(peer->device->dev, len);
- peer->rx_bytes += len;
+
+ pcpu_ptr = this_cpu_ptr(peer->pcpu_stats);
+
+ u64_stats_update_begin(&pcpu_ptr->syncp);
+ u64_stats_add(&pcpu_ptr->rx_bytes, len);
+ u64_stats_update_end(&pcpu_ptr->syncp);
}
#define SKB_TYPE_LE32(skb) (((struct message_header *)(skb)->data)->type)
diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c
index 0028ef17dc71..685ae6a0fb2c 100644
--- a/drivers/net/wireguard/socket.c
+++ b/drivers/net/wireguard/socket.c
@@ -166,6 +166,7 @@ static int send6(struct wg_device *wg, struct sk_buff *skb,
int wg_socket_send_skb_to_peer(struct wg_peer *peer, struct sk_buff *skb, u8 ds)
{
+ struct wg_peer_stats *pcpu_ptr;
size_t skb_len = skb->len;
int ret = -EAFNOSUPPORT;
@@ -178,8 +179,12 @@ int wg_socket_send_skb_to_peer(struct wg_peer *peer, struct sk_buff *skb, u8 ds)
&peer->endpoint_cache);
else
dev_kfree_skb(skb);
- if (likely(!ret))
- peer->tx_bytes += skb_len;
+ if (likely(!ret)) {
+ pcpu_ptr = this_cpu_ptr(peer->pcpu_stats);
+ u64_stats_update_begin(&pcpu_ptr->syncp);
+ u64_stats_add(&pcpu_ptr->tx_bytes, skb_len);
+ u64_stats_update_end(&pcpu_ptr->syncp);
+ }
read_unlock_bh(&peer->endpoint_lock);
return ret;
--
2.53.0
next prev parent reply other threads:[~2026-06-29 12:59 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-01 14:33 [syzbot] [wireguard?] KCSAN: data-race in wg_socket_send_skb_to_peer / wg_socket_send_skb_to_peer (9) syzbot
2026-06-22 19:34 ` Rafael Passos
2026-06-28 20:38 ` [PATCH] Wireguard: Fix data-race in rx/tx counter Rafael Passos
2026-06-28 21:02 ` Andrew Lunn
2026-06-29 2:34 ` Theodore Tso
2026-06-29 3:05 ` Rafael Passos
2026-06-29 12:59 ` Rafael Passos [this message]
2026-06-29 14:51 ` [PATCH v2] " Andrew Lunn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260629125913.2354450-1-rafael@rcpassos.me \
--to=rafael@rcpassos.me \
--cc=Jason@zx2c4.com \
--cc=andrew+netdev@lunn.ch \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=syzbot+9ca7674fa7521a3f1bc2@syzkaller.appspotmail.com \
--cc=syzkaller-bugs@googlegroups.com \
--cc=tytso@mit.edu \
--cc=wireguard@lists.zx2c4.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox