mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 17:48:58 +00:00
3888fa7880
Rather than using the clunky, old, slower wireguard-linux-compat out of tree module, this commit does a patch-by-patch backport of upstream's wireguard to 5.4. This specific backport is in widespread use, being part of SUSE's enterprise kernel, Oracle's enterprise kernel, Google's Android kernel, Gentoo's distro kernel, and probably more I've forgotten about. It's definately the "more proper" way of adding wireguard to a kernel than the ugly compat.h hell of the wireguard-linux-compat repo. And most importantly for OpenWRT, it allows using the same module configuration code for 5.10 as for 5.4, with no need for bifurcation. These patches are from the backport tree which is maintained in the open here: https://git.zx2c4.com/wireguard-linux/log/?h=backport-5.4.y I'll be sending PRs to update this as needed. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
300 lines
10 KiB
Diff
300 lines
10 KiB
Diff
From 4a25324891a32d080589a6e3a4dec2be2d9e3d60 Mon Sep 17 00:00:00 2001
|
|
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
|
|
Date: Tue, 23 Feb 2021 14:18:58 +0100
|
|
Subject: [PATCH 072/124] net: icmp: pass zeroed opts from icmp{,v6}_ndo_send
|
|
before sending
|
|
|
|
commit ee576c47db60432c37e54b1e2b43a8ca6d3a8dca upstream.
|
|
|
|
The icmp{,v6}_send functions make all sorts of use of skb->cb, casting
|
|
it with IPCB or IP6CB, assuming the skb to have come directly from the
|
|
inet layer. But when the packet comes from the ndo layer, especially
|
|
when forwarded, there's no telling what might be in skb->cb at that
|
|
point. As a result, the icmp sending code risks reading bogus memory
|
|
contents, which can result in nasty stack overflows such as this one
|
|
reported by a user:
|
|
|
|
panic+0x108/0x2ea
|
|
__stack_chk_fail+0x14/0x20
|
|
__icmp_send+0x5bd/0x5c0
|
|
icmp_ndo_send+0x148/0x160
|
|
|
|
In icmp_send, skb->cb is cast with IPCB and an ip_options struct is read
|
|
from it. The optlen parameter there is of particular note, as it can
|
|
induce writes beyond bounds. There are quite a few ways that can happen
|
|
in __ip_options_echo. For example:
|
|
|
|
// sptr/skb are attacker-controlled skb bytes
|
|
sptr = skb_network_header(skb);
|
|
// dptr/dopt points to stack memory allocated by __icmp_send
|
|
dptr = dopt->__data;
|
|
// sopt is the corrupt skb->cb in question
|
|
if (sopt->rr) {
|
|
optlen = sptr[sopt->rr+1]; // corrupt skb->cb + skb->data
|
|
soffset = sptr[sopt->rr+2]; // corrupt skb->cb + skb->data
|
|
// this now writes potentially attacker-controlled data, over
|
|
// flowing the stack:
|
|
memcpy(dptr, sptr+sopt->rr, optlen);
|
|
}
|
|
|
|
In the icmpv6_send case, the story is similar, but not as dire, as only
|
|
IP6CB(skb)->iif and IP6CB(skb)->dsthao are used. The dsthao case is
|
|
worse than the iif case, but it is passed to ipv6_find_tlv, which does
|
|
a bit of bounds checking on the value.
|
|
|
|
This is easy to simulate by doing a `memset(skb->cb, 0x41,
|
|
sizeof(skb->cb));` before calling icmp{,v6}_ndo_send, and it's only by
|
|
good fortune and the rarity of icmp sending from that context that we've
|
|
avoided reports like this until now. For example, in KASAN:
|
|
|
|
BUG: KASAN: stack-out-of-bounds in __ip_options_echo+0xa0e/0x12b0
|
|
Write of size 38 at addr ffff888006f1f80e by task ping/89
|
|
CPU: 2 PID: 89 Comm: ping Not tainted 5.10.0-rc7-debug+ #5
|
|
Call Trace:
|
|
dump_stack+0x9a/0xcc
|
|
print_address_description.constprop.0+0x1a/0x160
|
|
__kasan_report.cold+0x20/0x38
|
|
kasan_report+0x32/0x40
|
|
check_memory_region+0x145/0x1a0
|
|
memcpy+0x39/0x60
|
|
__ip_options_echo+0xa0e/0x12b0
|
|
__icmp_send+0x744/0x1700
|
|
|
|
Actually, out of the 4 drivers that do this, only gtp zeroed the cb for
|
|
the v4 case, while the rest did not. So this commit actually removes the
|
|
gtp-specific zeroing, while putting the code where it belongs in the
|
|
shared infrastructure of icmp{,v6}_ndo_send.
|
|
|
|
This commit fixes the issue by passing an empty IPCB or IP6CB along to
|
|
the functions that actually do the work. For the icmp_send, this was
|
|
already trivial, thanks to __icmp_send providing the plumbing function.
|
|
For icmpv6_send, this required a tiny bit of refactoring to make it
|
|
behave like the v4 case, after which it was straight forward.
|
|
|
|
Fixes: a2b78e9b2cac ("sunvnet: generate ICMP PTMUD messages for smaller port MTUs")
|
|
Reported-by: SinYu <liuxyon@gmail.com>
|
|
Reviewed-by: Willem de Bruijn <willemb@google.com>
|
|
Link: https://lore.kernel.org/netdev/CAF=yD-LOF116aHub6RMe8vB8ZpnrrnoTdqhobEx+bvoA8AsP0w@mail.gmail.com/T/
|
|
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
|
|
Link: https://lore.kernel.org/r/20210223131858.72082-1-Jason@zx2c4.com
|
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
|
[Jason: the gtp part didn't apply because it doesn't use icmp_ndo_send on 5.4]
|
|
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
|
|
---
|
|
include/linux/icmpv6.h | 17 ++++++++++++++---
|
|
include/linux/ipv6.h | 1 -
|
|
include/net/icmp.h | 6 +++++-
|
|
net/ipv4/icmp.c | 5 +++--
|
|
net/ipv6/icmp.c | 16 ++++++++--------
|
|
net/ipv6/ip6_icmp.c | 12 +++++++-----
|
|
6 files changed, 37 insertions(+), 20 deletions(-)
|
|
|
|
--- a/include/linux/icmpv6.h
|
|
+++ b/include/linux/icmpv6.h
|
|
@@ -3,6 +3,7 @@
|
|
#define _LINUX_ICMPV6_H
|
|
|
|
#include <linux/skbuff.h>
|
|
+#include <linux/ipv6.h>
|
|
#include <uapi/linux/icmpv6.h>
|
|
|
|
static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb)
|
|
@@ -13,10 +14,16 @@ static inline struct icmp6hdr *icmp6_hdr
|
|
#include <linux/netdevice.h>
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
-extern void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info);
|
|
+extern void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
|
+ const struct inet6_skb_parm *parm);
|
|
|
|
+static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
|
|
+{
|
|
+ __icmpv6_send(skb, type, code, info, IP6CB(skb));
|
|
+}
|
|
typedef void ip6_icmp_send_t(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
|
- const struct in6_addr *force_saddr);
|
|
+ const struct in6_addr *force_saddr,
|
|
+ const struct inet6_skb_parm *parm);
|
|
extern int inet6_register_icmp_sender(ip6_icmp_send_t *fn);
|
|
extern int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn);
|
|
int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
|
|
@@ -25,7 +32,11 @@ int ip6_err_gen_icmpv6_unreach(struct sk
|
|
#if IS_ENABLED(CONFIG_NF_NAT)
|
|
void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info);
|
|
#else
|
|
-#define icmpv6_ndo_send icmpv6_send
|
|
+static inline void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
|
|
+{
|
|
+ struct inet6_skb_parm parm = { 0 };
|
|
+ __icmpv6_send(skb_in, type, code, info, &parm);
|
|
+}
|
|
#endif
|
|
|
|
#else
|
|
--- a/include/linux/ipv6.h
|
|
+++ b/include/linux/ipv6.h
|
|
@@ -83,7 +83,6 @@ struct ipv6_params {
|
|
__s32 autoconf;
|
|
};
|
|
extern struct ipv6_params ipv6_defaults;
|
|
-#include <linux/icmpv6.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/udp.h>
|
|
|
|
--- a/include/net/icmp.h
|
|
+++ b/include/net/icmp.h
|
|
@@ -46,7 +46,11 @@ static inline void icmp_send(struct sk_b
|
|
#if IS_ENABLED(CONFIG_NF_NAT)
|
|
void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info);
|
|
#else
|
|
-#define icmp_ndo_send icmp_send
|
|
+static inline void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
|
|
+{
|
|
+ struct ip_options opts = { 0 };
|
|
+ __icmp_send(skb_in, type, code, info, &opts);
|
|
+}
|
|
#endif
|
|
|
|
int icmp_rcv(struct sk_buff *skb);
|
|
--- a/net/ipv4/icmp.c
|
|
+++ b/net/ipv4/icmp.c
|
|
@@ -755,13 +755,14 @@ EXPORT_SYMBOL(__icmp_send);
|
|
void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
|
|
{
|
|
struct sk_buff *cloned_skb = NULL;
|
|
+ struct ip_options opts = { 0 };
|
|
enum ip_conntrack_info ctinfo;
|
|
struct nf_conn *ct;
|
|
__be32 orig_ip;
|
|
|
|
ct = nf_ct_get(skb_in, &ctinfo);
|
|
if (!ct || !(ct->status & IPS_SRC_NAT)) {
|
|
- icmp_send(skb_in, type, code, info);
|
|
+ __icmp_send(skb_in, type, code, info, &opts);
|
|
return;
|
|
}
|
|
|
|
@@ -776,7 +777,7 @@ void icmp_ndo_send(struct sk_buff *skb_i
|
|
|
|
orig_ip = ip_hdr(skb_in)->saddr;
|
|
ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip;
|
|
- icmp_send(skb_in, type, code, info);
|
|
+ __icmp_send(skb_in, type, code, info, &opts);
|
|
ip_hdr(skb_in)->saddr = orig_ip;
|
|
out:
|
|
consume_skb(cloned_skb);
|
|
--- a/net/ipv6/icmp.c
|
|
+++ b/net/ipv6/icmp.c
|
|
@@ -312,10 +312,9 @@ static int icmpv6_getfrag(void *from, ch
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
|
-static void mip6_addr_swap(struct sk_buff *skb)
|
|
+static void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt)
|
|
{
|
|
struct ipv6hdr *iph = ipv6_hdr(skb);
|
|
- struct inet6_skb_parm *opt = IP6CB(skb);
|
|
struct ipv6_destopt_hao *hao;
|
|
struct in6_addr tmp;
|
|
int off;
|
|
@@ -332,7 +331,7 @@ static void mip6_addr_swap(struct sk_buf
|
|
}
|
|
}
|
|
#else
|
|
-static inline void mip6_addr_swap(struct sk_buff *skb) {}
|
|
+static inline void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt) {}
|
|
#endif
|
|
|
|
static struct dst_entry *icmpv6_route_lookup(struct net *net,
|
|
@@ -427,7 +426,8 @@ static int icmp6_iif(const struct sk_buf
|
|
* Send an ICMP message in response to a packet in error
|
|
*/
|
|
static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
|
- const struct in6_addr *force_saddr)
|
|
+ const struct in6_addr *force_saddr,
|
|
+ const struct inet6_skb_parm *parm)
|
|
{
|
|
struct inet6_dev *idev = NULL;
|
|
struct ipv6hdr *hdr = ipv6_hdr(skb);
|
|
@@ -520,7 +520,7 @@ static void icmp6_send(struct sk_buff *s
|
|
if (!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, type))
|
|
goto out_bh_enable;
|
|
|
|
- mip6_addr_swap(skb);
|
|
+ mip6_addr_swap(skb, parm);
|
|
|
|
memset(&fl6, 0, sizeof(fl6));
|
|
fl6.flowi6_proto = IPPROTO_ICMPV6;
|
|
@@ -605,7 +605,7 @@ out_bh_enable:
|
|
*/
|
|
void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos)
|
|
{
|
|
- icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL);
|
|
+ icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL, IP6CB(skb));
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
@@ -662,10 +662,10 @@ int ip6_err_gen_icmpv6_unreach(struct sk
|
|
}
|
|
if (type == ICMP_TIME_EXCEEDED)
|
|
icmp6_send(skb2, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
|
|
- info, &temp_saddr);
|
|
+ info, &temp_saddr, IP6CB(skb2));
|
|
else
|
|
icmp6_send(skb2, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH,
|
|
- info, &temp_saddr);
|
|
+ info, &temp_saddr, IP6CB(skb2));
|
|
if (rt)
|
|
ip6_rt_put(rt);
|
|
|
|
--- a/net/ipv6/ip6_icmp.c
|
|
+++ b/net/ipv6/ip6_icmp.c
|
|
@@ -31,7 +31,8 @@ int inet6_unregister_icmp_sender(ip6_icm
|
|
}
|
|
EXPORT_SYMBOL(inet6_unregister_icmp_sender);
|
|
|
|
-void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
|
|
+void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
|
+ const struct inet6_skb_parm *parm)
|
|
{
|
|
ip6_icmp_send_t *send;
|
|
|
|
@@ -40,16 +41,17 @@ void icmpv6_send(struct sk_buff *skb, u8
|
|
|
|
if (!send)
|
|
goto out;
|
|
- send(skb, type, code, info, NULL);
|
|
+ send(skb, type, code, info, NULL, parm);
|
|
out:
|
|
rcu_read_unlock();
|
|
}
|
|
-EXPORT_SYMBOL(icmpv6_send);
|
|
+EXPORT_SYMBOL(__icmpv6_send);
|
|
|
|
#if IS_ENABLED(CONFIG_NF_NAT)
|
|
#include <net/netfilter/nf_conntrack.h>
|
|
void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
|
|
{
|
|
+ struct inet6_skb_parm parm = { 0 };
|
|
struct sk_buff *cloned_skb = NULL;
|
|
enum ip_conntrack_info ctinfo;
|
|
struct in6_addr orig_ip;
|
|
@@ -57,7 +59,7 @@ void icmpv6_ndo_send(struct sk_buff *skb
|
|
|
|
ct = nf_ct_get(skb_in, &ctinfo);
|
|
if (!ct || !(ct->status & IPS_SRC_NAT)) {
|
|
- icmpv6_send(skb_in, type, code, info);
|
|
+ __icmpv6_send(skb_in, type, code, info, &parm);
|
|
return;
|
|
}
|
|
|
|
@@ -72,7 +74,7 @@ void icmpv6_ndo_send(struct sk_buff *skb
|
|
|
|
orig_ip = ipv6_hdr(skb_in)->saddr;
|
|
ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
|
|
- icmpv6_send(skb_in, type, code, info);
|
|
+ __icmpv6_send(skb_in, type, code, info, &parm);
|
|
ipv6_hdr(skb_in)->saddr = orig_ip;
|
|
out:
|
|
consume_skb(cloned_skb);
|