wireguard: allowedips: don't corrupt stack when detecting overflow
authorJason A. Donenfeld <Jason@zx2c4.com>
Tue, 2 Aug 2022 12:56:12 +0000 (14:56 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Aug 2022 12:23:37 +0000 (14:23 +0200)
[ Upstream commit c31b14d86dfe7174361e8c6e5df6c2c3a4d5918c ]

In case push_rcu() and related functions are buggy, there's a
WARN_ON(len >= 128), which the selftest tries to hit by being tricky. In
case it is hit, we shouldn't corrupt the kernel's stack, though;
otherwise it may be hard to even receive the report that it's buggy. So
conditionalize the stack write based on that WARN_ON()'s return value.

Note that this never *actually* happens anyway. The WARN_ON() in the
first place is bounded by IS_ENABLED(DEBUG), and isn't expected to ever
actually hit. This is just a debugging sanity check.

Additionally, hoist the constant 128 into a named enum,
MAX_ALLOWEDIPS_BITS, so that it's clear why this value is chosen.

Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/all/CAHk-=wjJZGA6w_DxA+k7Ejbqsq+uGK==koPai3sqdsfJqemvag@mail.gmail.com/
Fixes: e7096c131e51 ("net: WireGuard secure network tunnel")
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/wireguard/allowedips.c
drivers/net/wireguard/selftest/allowedips.c

index 9a4c8ff32d9dd9407ec50591a44008570f4e7411..5bf7822c53f18ab75f1b256a3e64ab14f161c825 100644 (file)
@@ -6,6 +6,8 @@
 #include "allowedips.h"
 #include "peer.h"
 
+enum { MAX_ALLOWEDIPS_BITS = 128 };
+
 static struct kmem_cache *node_cache;
 
 static void swap_endian(u8 *dst, const u8 *src, u8 bits)
@@ -40,7 +42,8 @@ static void push_rcu(struct allowedips_node **stack,
                     struct allowedips_node __rcu *p, unsigned int *len)
 {
        if (rcu_access_pointer(p)) {
-               WARN_ON(IS_ENABLED(DEBUG) && *len >= 128);
+               if (WARN_ON(IS_ENABLED(DEBUG) && *len >= MAX_ALLOWEDIPS_BITS))
+                       return;
                stack[(*len)++] = rcu_dereference_raw(p);
        }
 }
@@ -52,7 +55,7 @@ static void node_free_rcu(struct rcu_head *rcu)
 
 static void root_free_rcu(struct rcu_head *rcu)
 {
-       struct allowedips_node *node, *stack[128] = {
+       struct allowedips_node *node, *stack[MAX_ALLOWEDIPS_BITS] = {
                container_of(rcu, struct allowedips_node, rcu) };
        unsigned int len = 1;
 
@@ -65,7 +68,7 @@ static void root_free_rcu(struct rcu_head *rcu)
 
 static void root_remove_peer_lists(struct allowedips_node *root)
 {
-       struct allowedips_node *node, *stack[128] = { root };
+       struct allowedips_node *node, *stack[MAX_ALLOWEDIPS_BITS] = { root };
        unsigned int len = 1;
 
        while (len > 0 && (node = stack[--len])) {
index e173204ae7d78d48d730ea34b36f751added0abe..41db10f9be4985619b21a0922159ad91f87a803b 100644 (file)
@@ -593,10 +593,10 @@ bool __init wg_allowedips_selftest(void)
        wg_allowedips_remove_by_peer(&t, a, &mutex);
        test_negative(4, a, 192, 168, 0, 1);
 
-       /* These will hit the WARN_ON(len >= 128) in free_node if something
-        * goes wrong.
+       /* These will hit the WARN_ON(len >= MAX_ALLOWEDIPS_BITS) in free_node
+        * if something goes wrong.
         */
-       for (i = 0; i < 128; ++i) {
+       for (i = 0; i < MAX_ALLOWEDIPS_BITS; ++i) {
                part = cpu_to_be64(~(1LLU << (i % 64)));
                memset(&ip, 0xff, 16);
                memcpy((u8 *)&ip + (i < 64) * 8, &part, 8);