summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2017-10-11 03:07:40 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2017-10-11 15:50:29 +0200
commitf57c2d64bfc646c16fd1f30c8e3557183f8a5369 (patch)
tree8d5c9f497b1070e757454fabc37ed7749b49be2c
parent8f03a5a22c3de97473a562a7cfe76c6fde674b0e (diff)
receive: disable bh before using stats seq lock
Otherwise we might get a situation like this: CPU0 CPU1 ---- ---- lock(tstats lock); local_irq_disable(); lock(queue lock); lock(tstats lock); <Interrupt> lock(queue lock); CPU1 is waiting for CPU0 to release tstats lock. But CPU0, in the interrupt handler, is waiting for CPU1 to release queue lock. The solution is to disable interrupts on CPU0, so that this can't happen. Note that this only affects 32-bit, since u64_stats_update_begin nops out on native 64-bit platforms. Reported-by: René van Dorst <opensource@vdorst.com> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--src/receive.c4
1 files changed, 4 insertions, 0 deletions
diff --git a/src/receive.c b/src/receive.c
index 3ad790b..6efbc08 100644
--- a/src/receive.c
+++ b/src/receive.c
@@ -13,6 +13,7 @@
#include <linux/udp.h>
#include <net/ip_tunnels.h>
+/* Must be called with bh disabled. */
static inline void rx_stats(struct wireguard_peer *peer, size_t len)
{
struct pcpu_sw_netstats *tstats = get_cpu_ptr(peer->device->dev->tstats);
@@ -150,7 +151,10 @@ static void receive_handshake_packet(struct wireguard_device *wg, struct sk_buff
BUG_ON(!peer);
+ local_bh_disable();
rx_stats(peer, skb->len);
+ local_bh_enable();
+
timers_any_authenticated_packet_received(peer);
timers_any_authenticated_packet_traversal(peer);
peer_put(peer);