diff options
author | Willem de Bruijn <willemb@google.com> | 2016-07-12 18:18:56 -0400 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2017-02-23 03:54:46 +0000 |
commit | d0fb92f2ca9b8c3ba10047b5e134cd7d1459cc1c (patch) | |
tree | c604ee27806ef2a3ad809d5f80658b460f7ad9b0 | |
parent | bed7167a188ec74018825232eeee67e2032275f8 (diff) | |
download | linux-stable-d0fb92f2ca9b8c3ba10047b5e134cd7d1459cc1c.tar.gz linux-stable-d0fb92f2ca9b8c3ba10047b5e134cd7d1459cc1c.tar.bz2 linux-stable-d0fb92f2ca9b8c3ba10047b5e134cd7d1459cc1c.zip |
rose: limit sk_filter trim to payload
commit f4979fcea7fd36d8e2f556abef86f80e0d5af1ba upstream.
Sockets can have a filter program attached that drops or trims
incoming packets based on the filter program return value.
Rose requires data packets to have at least ROSE_MIN_LEN bytes. It
verifies this on arrival in rose_route_frame and unconditionally pulls
the bytes in rose_recvmsg. The filter can trim packets to below this
value in-between, causing pull to fail, leaving the partial header at
the time of skb_copy_datagram_msg.
Place a lower bound on the size to which sk_filter may trim packets
by introducing sk_filter_trim_cap and call this for rose packets.
Signed-off-by: Willem de Bruijn <willemb@google.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
[bwh: Backported to 3.16: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r-- | include/linux/filter.h | 6 | ||||
-rw-r--r-- | net/core/filter.c | 10 | ||||
-rw-r--r-- | net/rose/rose_in.c | 3 |
3 files changed, 12 insertions, 7 deletions
diff --git a/include/linux/filter.h b/include/linux/filter.h index 96509e579d21..bdc6f86e7897 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -346,7 +346,11 @@ static inline unsigned int sk_filter_size(unsigned int proglen) #define sk_filter_proglen(fprog) \ (fprog->len * sizeof(fprog->filter[0])) -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); +} void sk_filter_select_runtime(struct sk_filter *fp); void sk_filter_free(struct sk_filter *fp); diff --git a/net/core/filter.c b/net/core/filter.c index b275c5559dc0..fd6da397ea8c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -94,9 +94,10 @@ static inline void *load_pointer(const struct sk_buff *skb, int k, } /** - * 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 filter code and then cut skb->data to correct size returned by * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller @@ -105,7 +106,7 @@ static inline void *load_pointer(const struct sk_buff *skb, int k, * 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; @@ -126,14 +127,13 @@ int sk_filter(struct sock *sk, struct sk_buff *skb) filter = rcu_dereference(sk->sk_filter); if (filter) { unsigned int pkt_len = SK_RUN_FILTER(filter, 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); /* Base function for offset calculation. Needs to go into .text section, * therefore keeping it non-static as well; will also be used by JITs diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c index 79c4abcfa6b4..0a6394754e81 100644 --- a/net/rose/rose_in.c +++ b/net/rose/rose_in.c @@ -164,7 +164,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety rose_frames_acked(sk, nr); if (ns == rose->vr) { rose_start_idletimer(sk); - if (sock_queue_rcv_skb(sk, skb) == 0) { + if (sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) == 0 && + __sock_queue_rcv_skb(sk, skb) == 0) { rose->vr = (rose->vr + 1) % ROSE_MODULUS; queued = 1; } else { |