mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
tcp: take care of truncations done by sk_filter()
[ Upstream commit ac6e780070 ]
With syzkaller help, Marco Grassi found a bug in TCP stack,
crashing in tcp_collapse()
Root cause is that sk_filter() can truncate the incoming skb,
but TCP stack was not really expecting this to happen.
It probably was expecting a simple DROP or ACCEPT behavior.
We first need to make sure no part of TCP header could be removed.
Then we need to adjust TCP_SKB_CB(skb)->end_seq
Many thanks to syzkaller team and Marco for giving us a reproducer.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Marco Grassi <marco.gra@gmail.com>
Reported-by: Vladis Dronov <vdronov@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
ae9e052a58
commit
225a24ae97
@@ -421,7 +421,11 @@ static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_SET_MODULE_RONX */
|
||||
|
||||
int sk_filter(struct sock *sk, struct sk_buff *skb);
|
||||
int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap);
|
||||
static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
return sk_filter_trim_cap(sk, skb, 1);
|
||||
}
|
||||
|
||||
int bpf_prog_select_runtime(struct bpf_prog *fp);
|
||||
void bpf_prog_free(struct bpf_prog *fp);
|
||||
|
||||
@@ -1156,6 +1156,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp)
|
||||
}
|
||||
|
||||
bool tcp_prequeue(struct sock *sk, struct sk_buff *skb);
|
||||
int tcp_filter(struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
#undef STATE_TRACE
|
||||
|
||||
|
||||
@@ -52,9 +52,10 @@
|
||||
#include <net/dst.h>
|
||||
|
||||
/**
|
||||
* sk_filter - run a packet through a socket filter
|
||||
* sk_filter_trim_cap - run a packet through a socket filter
|
||||
* @sk: sock associated with &sk_buff
|
||||
* @skb: buffer to filter
|
||||
* @cap: limit on how short the eBPF program may trim the packet
|
||||
*
|
||||
* Run the eBPF program and then cut skb->data to correct size returned by
|
||||
* the program. If pkt_len is 0 we toss packet. If skb->len is smaller
|
||||
@@ -63,7 +64,7 @@
|
||||
* be accepted or -EPERM if the packet should be tossed.
|
||||
*
|
||||
*/
|
||||
int sk_filter(struct sock *sk, struct sk_buff *skb)
|
||||
int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)
|
||||
{
|
||||
int err;
|
||||
struct sk_filter *filter;
|
||||
@@ -84,14 +85,13 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
|
||||
filter = rcu_dereference(sk->sk_filter);
|
||||
if (filter) {
|
||||
unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
|
||||
|
||||
err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
|
||||
err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(sk_filter);
|
||||
EXPORT_SYMBOL(sk_filter_trim_cap);
|
||||
|
||||
static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
|
||||
{
|
||||
|
||||
@@ -1533,6 +1533,21 @@ bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
|
||||
}
|
||||
EXPORT_SYMBOL(tcp_prequeue);
|
||||
|
||||
int tcp_filter(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct tcphdr *th = (struct tcphdr *)skb->data;
|
||||
unsigned int eaten = skb->len;
|
||||
int err;
|
||||
|
||||
err = sk_filter_trim_cap(sk, skb, th->doff * 4);
|
||||
if (!err) {
|
||||
eaten -= skb->len;
|
||||
TCP_SKB_CB(skb)->end_seq -= eaten;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(tcp_filter);
|
||||
|
||||
/*
|
||||
* From tcp_input.c
|
||||
*/
|
||||
@@ -1638,8 +1653,10 @@ process:
|
||||
|
||||
nf_reset(skb);
|
||||
|
||||
if (sk_filter(sk, skb))
|
||||
if (tcp_filter(sk, skb))
|
||||
goto discard_and_relse;
|
||||
th = (const struct tcphdr *)skb->data;
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
skb->dev = NULL;
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
|
||||
if (skb->protocol == htons(ETH_P_IP))
|
||||
return tcp_v4_do_rcv(sk, skb);
|
||||
|
||||
if (sk_filter(sk, skb))
|
||||
if (tcp_filter(sk, skb))
|
||||
goto discard;
|
||||
|
||||
/*
|
||||
@@ -1438,8 +1438,10 @@ process:
|
||||
if (tcp_v6_inbound_md5_hash(sk, skb))
|
||||
goto discard_and_relse;
|
||||
|
||||
if (sk_filter(sk, skb))
|
||||
if (tcp_filter(sk, skb))
|
||||
goto discard_and_relse;
|
||||
th = (const struct tcphdr *)skb->data;
|
||||
hdr = ipv6_hdr(skb);
|
||||
|
||||
skb->dev = NULL;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user