netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net 0/2] limit sk_filter trim to payload
@ 2016-07-12 22:18 Willem de Bruijn
  2016-07-12 22:18 ` [PATCH net 1/2] rose: " Willem de Bruijn
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Willem de Bruijn @ 2016-07-12 22:18 UTC (permalink / raw)
  To: netdev
  Cc: alexei.starovoitov, daniel, davem, hannes, eric.dumazet,
	Willem de Bruijn

From: Willem de Bruijn <willemb@google.com>

Sockets can apply a filter to incoming packets to drop or trim them.
Fix two codepaths that call skb_pull/__skb_pull after sk_filter
without checking for packet length.

Reading beyond skb->tail after trimming happens in more codepaths, but
safety of reading in the linear segment is based on minimum allocation
size (MAX_HEADER, GRO_MAX_HEAD, ..).

Willem de Bruijn (2):
  rose: limit sk_filter trim to payload
  dccp: limit sk_filter trim to payload

 include/linux/filter.h |  6 +++++-
 include/net/sock.h     |  8 +++++++-
 net/core/filter.c      | 10 +++++-----
 net/core/sock.c        |  7 ++++---
 net/dccp/ipv4.c        |  2 +-
 net/dccp/ipv6.c        |  2 +-
 net/rose/rose_in.c     |  3 ++-
 7 files changed, 25 insertions(+), 13 deletions(-)

-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH net 1/2] rose: limit sk_filter trim to payload
  2016-07-12 22:18 [PATCH net 0/2] limit sk_filter trim to payload Willem de Bruijn
@ 2016-07-12 22:18 ` Willem de Bruijn
  2016-07-12 22:49   ` Daniel Borkmann
  2016-07-12 22:18 ` [PATCH net 2/2] dccp: " Willem de Bruijn
  2016-07-13 18:57 ` [PATCH net 0/2] " David Miller
  2 siblings, 1 reply; 7+ messages in thread
From: Willem de Bruijn @ 2016-07-12 22:18 UTC (permalink / raw)
  To: netdev
  Cc: alexei.starovoitov, daniel, davem, hannes, eric.dumazet,
	Willem de Bruijn

From: Willem de Bruijn <willemb@google.com>

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>

---

No Fixes tag, because this codepath goes back to the start of the
git repo history.
---
 include/linux/filter.h |  6 +++++-
 net/core/filter.c      | 10 +++++-----
 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 6fc31ef..8f74f3d 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -467,7 +467,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);
+}
 
 struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err);
 void bpf_prog_free(struct bpf_prog *fp);
diff --git a/net/core/filter.c b/net/core/filter.c
index c4b330c..e759d90 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -53,9 +53,10 @@
 #include <net/sock_reuseport.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
@@ -64,7 +65,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;
@@ -85,14 +86,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)
 {
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 79c4abc..0a63947 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 {
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH net 2/2] dccp: limit sk_filter trim to payload
  2016-07-12 22:18 [PATCH net 0/2] limit sk_filter trim to payload Willem de Bruijn
  2016-07-12 22:18 ` [PATCH net 1/2] rose: " Willem de Bruijn
@ 2016-07-12 22:18 ` Willem de Bruijn
  2016-07-12 22:53   ` Daniel Borkmann
  2016-07-13 18:57 ` [PATCH net 0/2] " David Miller
  2 siblings, 1 reply; 7+ messages in thread
From: Willem de Bruijn @ 2016-07-12 22:18 UTC (permalink / raw)
  To: netdev
  Cc: alexei.starovoitov, daniel, davem, hannes, eric.dumazet,
	Willem de Bruijn

From: Willem de Bruijn <willemb@google.com>

Dccp verifies packet integrity, including length, at initial rcv in
dccp_invalid_packet, later pulls headers in dccp_enqueue_skb.

A call to sk_filter in-between can cause __skb_pull to wrap skb->len.
skb_copy_datagram_msg interprets this as a negative value, so
(correctly) fails with EFAULT. The negative length is reported in
ioctl SIOCINQ or possibly in a DCCP_WARN in dccp_close.

Introduce an sk_receive_skb variant that caps how small a filter
program can trim packets, and call this in dccp with the header
length. Excessively trimmed packets are now processed normally and
queued for reception as 0B payloads.

Fixes: 7c657876b63c ("[DCCP]: Initial implementation")
Signed-off-by: Willem de Bruijn <willemb@google.com>
---
 include/net/sock.h | 8 +++++++-
 net/core/sock.c    | 7 ++++---
 net/dccp/ipv4.c    | 2 +-
 net/dccp/ipv6.c    | 2 +-
 4 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/include/net/sock.h b/include/net/sock.h
index 649d2a8..ff5be7e 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1576,7 +1576,13 @@ static inline void sock_put(struct sock *sk)
  */
 void sock_gen_put(struct sock *sk);
 
-int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested);
+int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested,
+		     unsigned int trim_cap);
+static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+				 const int nested)
+{
+	return __sk_receive_skb(sk, skb, nested, 1);
+}
 
 static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
 {
diff --git a/net/core/sock.c b/net/core/sock.c
index b7f1263..25dab8b 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -452,11 +452,12 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(sock_queue_rcv_skb);
 
-int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
+int __sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+		     const int nested, unsigned int trim_cap)
 {
 	int rc = NET_RX_SUCCESS;
 
-	if (sk_filter(sk, skb))
+	if (sk_filter_trim_cap(sk, skb, trim_cap))
 		goto discard_and_relse;
 
 	skb->dev = NULL;
@@ -492,7 +493,7 @@ discard_and_relse:
 	kfree_skb(skb);
 	goto out;
 }
-EXPORT_SYMBOL(sk_receive_skb);
+EXPORT_SYMBOL(__sk_receive_skb);
 
 struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
 {
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 25dd25b..345a3ae 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -868,7 +868,7 @@ lookup:
 		goto discard_and_relse;
 	nf_reset(skb);
 
-	return sk_receive_skb(sk, skb, 1);
+	return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4);
 
 no_dccp_socket:
 	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index d176f4e..3ff137d 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -732,7 +732,7 @@ lookup:
 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 		goto discard_and_relse;
 
-	return sk_receive_skb(sk, skb, 1) ? -1 : 0;
+	return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4) ? -1 : 0;
 
 no_dccp_socket:
 	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH net 1/2] rose: limit sk_filter trim to payload
@ 2016-07-12 22:30 Willem de Bruijn
  0 siblings, 0 replies; 7+ messages in thread
From: Willem de Bruijn @ 2016-07-12 22:30 UTC (permalink / raw)
  To: netdev
  Cc: alexei.starovoitov, daniel, davem, hannes, eric.dumazet,
	Willem de Bruijn

From: Willem de Bruijn <willemb@google.com>

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>

---

No Fixes tag, because this codepath goes back to the start of the
git repo history.
---
 include/linux/filter.h |  6 +++++-
 net/core/filter.c      | 10 +++++-----
 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 6fc31ef..8f74f3d 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -467,7 +467,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);
+}
 
 struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err);
 void bpf_prog_free(struct bpf_prog *fp);
diff --git a/net/core/filter.c b/net/core/filter.c
index c4b330c..e759d90 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -53,9 +53,10 @@
 #include <net/sock_reuseport.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
@@ -64,7 +65,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;
@@ -85,14 +86,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)
 {
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 79c4abc..0a63947 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 {
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH net 1/2] rose: limit sk_filter trim to payload
  2016-07-12 22:18 ` [PATCH net 1/2] rose: " Willem de Bruijn
@ 2016-07-12 22:49   ` Daniel Borkmann
  0 siblings, 0 replies; 7+ messages in thread
From: Daniel Borkmann @ 2016-07-12 22:49 UTC (permalink / raw)
  To: Willem de Bruijn, netdev
  Cc: alexei.starovoitov, davem, hannes, eric.dumazet, Willem de Bruijn

On 07/13/2016 12:18 AM, Willem de Bruijn wrote:
> From: Willem de Bruijn <willemb@google.com>
>
> 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>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net 2/2] dccp: limit sk_filter trim to payload
  2016-07-12 22:18 ` [PATCH net 2/2] dccp: " Willem de Bruijn
@ 2016-07-12 22:53   ` Daniel Borkmann
  0 siblings, 0 replies; 7+ messages in thread
From: Daniel Borkmann @ 2016-07-12 22:53 UTC (permalink / raw)
  To: Willem de Bruijn, netdev
  Cc: alexei.starovoitov, davem, hannes, eric.dumazet, Willem de Bruijn

On 07/13/2016 12:18 AM, Willem de Bruijn wrote:
> From: Willem de Bruijn <willemb@google.com>
>
> Dccp verifies packet integrity, including length, at initial rcv in
> dccp_invalid_packet, later pulls headers in dccp_enqueue_skb.
>
> A call to sk_filter in-between can cause __skb_pull to wrap skb->len.
> skb_copy_datagram_msg interprets this as a negative value, so
> (correctly) fails with EFAULT. The negative length is reported in
> ioctl SIOCINQ or possibly in a DCCP_WARN in dccp_close.
>
> Introduce an sk_receive_skb variant that caps how small a filter
> program can trim packets, and call this in dccp with the header
> length. Excessively trimmed packets are now processed normally and
> queued for reception as 0B payloads.
>
> Fixes: 7c657876b63c ("[DCCP]: Initial implementation")
> Signed-off-by: Willem de Bruijn <willemb@google.com>

LGTM, thanks Willem!

Acked-by: Daniel Borkmann <daniel@iogearbox.net>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net 0/2] limit sk_filter trim to payload
  2016-07-12 22:18 [PATCH net 0/2] limit sk_filter trim to payload Willem de Bruijn
  2016-07-12 22:18 ` [PATCH net 1/2] rose: " Willem de Bruijn
  2016-07-12 22:18 ` [PATCH net 2/2] dccp: " Willem de Bruijn
@ 2016-07-13 18:57 ` David Miller
  2 siblings, 0 replies; 7+ messages in thread
From: David Miller @ 2016-07-13 18:57 UTC (permalink / raw)
  To: willemdebruijn.kernel
  Cc: netdev, alexei.starovoitov, daniel, hannes, eric.dumazet, willemb

From: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Date: Tue, 12 Jul 2016 18:18:55 -0400

> From: Willem de Bruijn <willemb@google.com>
> 
> Sockets can apply a filter to incoming packets to drop or trim them.
> Fix two codepaths that call skb_pull/__skb_pull after sk_filter
> without checking for packet length.
> 
> Reading beyond skb->tail after trimming happens in more codepaths, but
> safety of reading in the linear segment is based on minimum allocation
> size (MAX_HEADER, GRO_MAX_HEAD, ..).

Series applied and queued up for -stable, thanks.

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2016-07-13 18:57 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-07-12 22:18 [PATCH net 0/2] limit sk_filter trim to payload Willem de Bruijn
2016-07-12 22:18 ` [PATCH net 1/2] rose: " Willem de Bruijn
2016-07-12 22:49   ` Daniel Borkmann
2016-07-12 22:18 ` [PATCH net 2/2] dccp: " Willem de Bruijn
2016-07-12 22:53   ` Daniel Borkmann
2016-07-13 18:57 ` [PATCH net 0/2] " David Miller
  -- strict thread matches above, loose matches on Subject: below --
2016-07-12 22:30 [PATCH net 1/2] rose: " Willem de Bruijn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).