diff mbox

[1/2] rose: limit sk_filter trim to payload

Message ID 20170502164316.5365-2-dan.streetman@canonical.com
State New
Headers show

Commit Message

Dan Streetman May 2, 2017, 4:43 p.m. UTC
From: Willem de Bruijn <willemb@google.com>

BugLink: http://bugs.launchpad.net/bugs/1687107

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>
(backported from commit f4979fcea7fd36d8e2f556abef86f80e0d5af1ba)
[ddstreet: part of the upstream commit is required by the next commit;
           this leaves out upstream's change to net/rose/rose_in.c,
	   as that is not needed and requires additional backports]
Signed-off-by: Dan Streetman <dan.streetman@canonical.com>
---
 include/linux/filter.h | 7 ++++++-
 net/core/filter.c      | 9 +++++----
 2 files changed, 11 insertions(+), 5 deletions(-)
diff mbox

Patch

diff --git a/include/linux/filter.h b/include/linux/filter.h
index ff4e40cd45b1..049d93ee0076 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -41,7 +41,12 @@  static inline unsigned int sk_filter_size(unsigned int proglen)
 		   offsetof(struct sk_filter, insns[proglen]));
 }
 
-extern 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);
+}
+
 extern unsigned int sk_run_filter(const struct sk_buff *skb,
 				  const struct sock_filter *filter);
 extern int sk_unattached_filter_create(struct sk_filter **pfp,
diff --git a/net/core/filter.c b/net/core/filter.c
index ebce437678fc..f9857990769f 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -67,9 +67,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
@@ -78,7 +79,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;
@@ -100,13 +101,13 @@  int sk_filter(struct sock *sk, struct sk_buff *skb)
 	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);
 
 /**
  *	sk_run_filter - run a filter on a socket