All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net 0/3] validate variable length ll headers
@ 2016-03-04 20:44 Willem de Bruijn
  2016-03-04 20:44 ` [PATCH net 1/3] net: " Willem de Bruijn
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Willem de Bruijn @ 2016-03-04 20:44 UTC (permalink / raw)
  To: netdev
  Cc: davem, alan, hessu, martin.blumenstingl, linux-hams, Willem de Bruijn

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

Allow device-specific validation of link layer headers. Existing
checks drop all packets shorter than hard_header_len. For variable
length protocols, such packets can be valid.

patch 1 adds header_ops.validate and dev_validate_header
patch 2 replaces ll_header_truncated with dev_validate_header
patch 3 implements the protocol specific callback for AX25

Tested with a temporary eth_header_validate function. The AX25
code is compile-tested only at this point.

Willem de Bruijn (3):
  net: validate variable length ll headers
  packet: validate variable length ll headers
  ax25: add link layer header validation function

 include/linux/netdevice.h | 22 ++++++++++++++++++++--
 net/ax25/ax25_ip.c        | 15 +++++++++++++++
 net/packet/af_packet.c    | 38 +++++++++++++++++---------------------
 3 files changed, 52 insertions(+), 23 deletions(-)

-- 
2.7.0.rc3.207.g0ac5344

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

* [PATCH net 1/3] net: validate variable length ll headers
  2016-03-04 20:44 [PATCH net 0/3] validate variable length ll headers Willem de Bruijn
@ 2016-03-04 20:44 ` Willem de Bruijn
  2016-03-05  9:22   ` walter harms
  2016-03-04 20:44 ` [PATCH net 2/3] packet: " Willem de Bruijn
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Willem de Bruijn @ 2016-03-04 20:44 UTC (permalink / raw)
  To: netdev
  Cc: davem, alan, hessu, martin.blumenstingl, linux-hams, Willem de Bruijn

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

Netdevice parameter hard_header_len is variously interpreted both as
an upper and lower bound on link layer header length. The field is
used as upper bound when reserving room at allocation, as lower bound
when validating user input in PF_PACKET.

Clarify the definition to be maximum header length. For validation
of untrusted headers, add an optional validate member to header_ops.

Allow bypassing of validation by passing CAP_SYS_RAWIO, for instance
for deliberate testing of corrupt input. In this case, pad trailing
bytes, as some device drivers expect completely initialized headers.

See also http://comments.gmane.org/gmane.linux.network/401064

Signed-off-by: Willem de Bruijn <willemb@google.com>
---
 include/linux/netdevice.h | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 5440b7b..6d1d8f4 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -267,6 +267,7 @@ struct header_ops {
 	void	(*cache_update)(struct hh_cache *hh,
 				const struct net_device *dev,
 				const unsigned char *haddr);
+	bool	(*validate)(const char *ll_header, unsigned int len);
 };
 
 /* These flag bits are private to the generic network queueing
@@ -1420,8 +1421,7 @@ enum netdev_priv_flags {
  *	@dma:		DMA channel
  *	@mtu:		Interface MTU value
  *	@type:		Interface hardware type
- *	@hard_header_len: Hardware header length, which means that this is the
- *			  minimum size of a packet.
+ *	@hard_header_len: Maximum hardware header length.
  *
  *	@needed_headroom: Extra headroom the hardware may need, but not in all
  *			  cases can this be guaranteed
@@ -2627,6 +2627,24 @@ static inline int dev_parse_header(const struct sk_buff *skb,
 	return dev->header_ops->parse(skb, haddr);
 }
 
+/* ll_header must have at least hard_header_len allocated */
+static inline bool dev_validate_header(const struct net_device *dev,
+				       char *ll_header, int len)
+{
+	if (likely(len >= dev->hard_header_len))
+		return true;
+
+	if (capable(CAP_SYS_RAWIO)) {
+		memset(ll_header + len, 0, dev->hard_header_len - len);
+		return true;
+	}
+
+	if (dev->header_ops && dev->header_ops->validate)
+		return dev->header_ops->validate(ll_header, len);
+
+	return false;
+}
+
 typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len);
 int register_gifconf(unsigned int family, gifconf_func_t *gifconf);
 static inline int unregister_gifconf(unsigned int family)
-- 
2.7.0.rc3.207.g0ac5344

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

* [PATCH net 2/3] packet: validate variable length ll headers
  2016-03-04 20:44 [PATCH net 0/3] validate variable length ll headers Willem de Bruijn
  2016-03-04 20:44 ` [PATCH net 1/3] net: " Willem de Bruijn
@ 2016-03-04 20:44 ` Willem de Bruijn
  2016-03-07 17:38   ` Willem de Bruijn
  2016-03-04 20:44 ` [PATCH net 3/3] ax25: add link layer header validation function Willem de Bruijn
  2016-03-09 20:54 ` [PATCH net 0/3] validate variable length ll headers David Miller
  3 siblings, 1 reply; 7+ messages in thread
From: Willem de Bruijn @ 2016-03-04 20:44 UTC (permalink / raw)
  To: netdev
  Cc: davem, alan, hessu, martin.blumenstingl, linux-hams, Willem de Bruijn

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

Replace link layer header validation check ll_header_truncate with
more generic dev_validate_header.

Validation based on hard_header_len incorrectly drops valid packets
in variable length protocols, such as AX25. dev_validate_header
calls header_ops.validate for such protocols to ensure correctness
below hard_header_len.

See also http://comments.gmane.org/gmane.linux.network/401064

Signed-off-by: Willem de Bruijn <willemb@google.com>
---
 net/packet/af_packet.c | 38 +++++++++++++++++---------------------
 1 file changed, 17 insertions(+), 21 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 992396a..488678a 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1916,6 +1916,10 @@ retry:
 		goto retry;
 	}
 
+	if (!dev_validate_header(dev, skb->data, len)) {
+		err = -EINVAL;
+		goto out_unlock;
+	}
 	if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
 	    !packet_extra_vlan_len_allowed(dev, skb)) {
 		err = -EMSGSIZE;
@@ -2326,18 +2330,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
 	sock_wfree(skb);
 }
 
-static bool ll_header_truncated(const struct net_device *dev, int len)
-{
-	/* net device doesn't like empty head */
-	if (unlikely(len < dev->hard_header_len)) {
-		net_warn_ratelimited("%s: packet size is too short (%d < %d)\n",
-				     current->comm, len, dev->hard_header_len);
-		return true;
-	}
-
-	return false;
-}
-
 static void tpacket_set_protocol(const struct net_device *dev,
 				 struct sk_buff *skb)
 {
@@ -2420,14 +2412,14 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
 		if (unlikely(err < 0))
 			return -EINVAL;
 	} else if (dev->hard_header_len) {
-		if (ll_header_truncated(dev, tp_len))
-			return -EINVAL;
+		int hhlen = min_t(int, dev->hard_header_len, tp_len);
 
 		skb_push(skb, dev->hard_header_len);
-		err = skb_store_bits(skb, 0, data,
-				dev->hard_header_len);
+		err = skb_store_bits(skb, 0, data, hhlen);
 		if (unlikely(err))
 			return err;
+		if (!dev_validate_header(dev, skb->data, hhlen))
+			return -EINVAL;
 		if (!skb->protocol)
 			tpacket_set_protocol(dev, skb);
 
@@ -2758,14 +2750,12 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
 
 	skb_set_network_header(skb, reserve);
 
-	err = -EINVAL;
 	if (sock->type == SOCK_DGRAM) {
 		offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len);
-		if (unlikely(offset < 0))
-			goto out_free;
-	} else {
-		if (ll_header_truncated(dev, len))
+		if (unlikely(offset < 0)) {
+			err = -EINVAL;
 			goto out_free;
+		}
 	}
 
 	/* Returns -EFAULT on error */
@@ -2773,6 +2763,12 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
 	if (err)
 		goto out_free;
 
+	if (sock->type == SOCK_RAW &&
+	    !dev_validate_header(dev, skb->data, len)) {
+		err = -EINVAL;
+		goto out_free;
+	}
+
 	sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
 
 	if (!gso_type && (len > dev->mtu + reserve + extra_len) &&
-- 
2.7.0.rc3.207.g0ac5344

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

* [PATCH net 3/3] ax25: add link layer header validation function
  2016-03-04 20:44 [PATCH net 0/3] validate variable length ll headers Willem de Bruijn
  2016-03-04 20:44 ` [PATCH net 1/3] net: " Willem de Bruijn
  2016-03-04 20:44 ` [PATCH net 2/3] packet: " Willem de Bruijn
@ 2016-03-04 20:44 ` Willem de Bruijn
  2016-03-09 20:54 ` [PATCH net 0/3] validate variable length ll headers David Miller
  3 siblings, 0 replies; 7+ messages in thread
From: Willem de Bruijn @ 2016-03-04 20:44 UTC (permalink / raw)
  To: netdev
  Cc: davem, alan, hessu, martin.blumenstingl, linux-hams, Willem de Bruijn

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

As variable length protocol, AX25 fails link layer header validation
tests based on a minimum length. header_ops.validate allows protocols
to validate headers that are shorter than hard_header_len. Implement
this callback for AX25.

Compile tested only.

See also http://comments.gmane.org/gmane.linux.network/401064

Signed-off-by: Willem de Bruijn <willemb@google.com>
---
 net/ax25/ax25_ip.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c
index b563a3f..77bc20d 100644
--- a/net/ax25/ax25_ip.c
+++ b/net/ax25/ax25_ip.c
@@ -228,8 +228,23 @@ netdev_tx_t ax25_ip_xmit(struct sk_buff *skb)
 }
 #endif
 
+static bool ax25_validate_header(const char *header, unsigned int len)
+{
+	ax25_digi digi;
+
+	if (!len)
+		return false;
+
+	if (header[0] != 0)
+		return true;
+
+	return ax25_addr_parse(header + 1, len - 1, NULL, NULL, &digi, NULL,
+			       NULL);
+}
+
 const struct header_ops ax25_header_ops = {
 	.create = ax25_hard_header,
+	.validate = ax25_validate_header,
 };
 
 EXPORT_SYMBOL(ax25_header_ops);
-- 
2.7.0.rc3.207.g0ac5344

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

* Re: [PATCH net 1/3] net: validate variable length ll headers
  2016-03-04 20:44 ` [PATCH net 1/3] net: " Willem de Bruijn
@ 2016-03-05  9:22   ` walter harms
  0 siblings, 0 replies; 7+ messages in thread
From: walter harms @ 2016-03-05  9:22 UTC (permalink / raw)
  To: Willem de Bruijn
  Cc: netdev, davem, alan, hessu, martin.blumenstingl, linux-hams,
	Willem de Bruijn



Am 04.03.2016 21:44, schrieb Willem de Bruijn:
> From: Willem de Bruijn <willemb@google.com>
> 
> Netdevice parameter hard_header_len is variously interpreted both as
> an upper and lower bound on link layer header length. The field is
> used as upper bound when reserving room at allocation, as lower bound
> when validating user input in PF_PACKET.
> 
> Clarify the definition to be maximum header length. For validation
> of untrusted headers, add an optional validate member to header_ops.
> 
> Allow bypassing of validation by passing CAP_SYS_RAWIO, for instance
> for deliberate testing of corrupt input. In this case, pad trailing
> bytes, as some device drivers expect completely initialized headers.
> 
> See also http://comments.gmane.org/gmane.linux.network/401064
> 
> Signed-off-by: Willem de Bruijn <willemb@google.com>
> ---
>  include/linux/netdevice.h | 22 ++++++++++++++++++++--
>  1 file changed, 20 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 5440b7b..6d1d8f4 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -267,6 +267,7 @@ struct header_ops {
>  	void	(*cache_update)(struct hh_cache *hh,
>  				const struct net_device *dev,
>  				const unsigned char *haddr);
> +	bool	(*validate)(const char *ll_header, unsigned int len);
>  };
>  
>  /* These flag bits are private to the generic network queueing
> @@ -1420,8 +1421,7 @@ enum netdev_priv_flags {
>   *	@dma:		DMA channel
>   *	@mtu:		Interface MTU value
>   *	@type:		Interface hardware type
> - *	@hard_header_len: Hardware header length, which means that this is the
> - *			  minimum size of a packet.
> + *	@hard_header_len: Maximum hardware header length.
>   *
>   *	@needed_headroom: Extra headroom the hardware may need, but not in all
>   *			  cases can this be guaranteed
> @@ -2627,6 +2627,24 @@ static inline int dev_parse_header(const struct sk_buff *skb,
>  	return dev->header_ops->parse(skb, haddr);
>  }
>  
> +/* ll_header must have at least hard_header_len allocated */
> +static inline bool dev_validate_header(const struct net_device *dev,
> +				       char *ll_header, int len)
> +{
> +	if (likely(len >= dev->hard_header_len))
> +		return true;
> +
> +	if (capable(CAP_SYS_RAWIO)) {
> +		memset(ll_header + len, 0, dev->hard_header_len - len);
> +		return true;
> +	}
> +
> +	if (dev->header_ops && dev->header_ops->validate)
> +		return dev->header_ops->validate(ll_header, len);
> +
> +	return false;
> +}
> +


you could use

real_len=dev->hard_header_len-len;

if (real_len < 0)
...
if (capable(CAP_SYS_RAWIO))
	memset(ll_header + len, 0,real_len);
..

IMHO that makes the code more clear.

re,
 wh



>  typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len);
>  int register_gifconf(unsigned int family, gifconf_func_t *gifconf);
>  static inline int unregister_gifconf(unsigned int family)

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

* Re: [PATCH net 2/3] packet: validate variable length ll headers
  2016-03-04 20:44 ` [PATCH net 2/3] packet: " Willem de Bruijn
@ 2016-03-07 17:38   ` Willem de Bruijn
  0 siblings, 0 replies; 7+ messages in thread
From: Willem de Bruijn @ 2016-03-07 17:38 UTC (permalink / raw)
  To: Network Development
  Cc: David Miller, Alan Cox, Heikki Hannikainen, Martin Blumenstingl,
	linux-hams, Willem de Bruijn

On Fri, Mar 4, 2016 at 3:44 PM, Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
> From: Willem de Bruijn <willemb@google.com>
>
> Replace link layer header validation check ll_header_truncate with
> more generic dev_validate_header.
>
> Validation based on hard_header_len incorrectly drops valid packets
> in variable length protocols, such as AX25. dev_validate_header
> calls header_ops.validate for such protocols to ensure correctness
> below hard_header_len.
>
> See also http://comments.gmane.org/gmane.linux.network/401064

Forgot to add

  Fixes 9c7077622dd9 ("packet: make packet_snd fail on len smaller
than l2 header")

This patch, if submitted to net, will have non-trivial merge conflicts
with net-next due to

  1d036d25e560 ("packet: tpacket_snd gso and checksum offload")

I maintain a patchset on top of net-next. Can send that to supersede
this one to resolve it (at the cost of only fixing the bug in the later
kernel version).

> Signed-off-by: Willem de Bruijn <willemb@google.com>
> ---
>  net/packet/af_packet.c | 38 +++++++++++++++++---------------------
>  1 file changed, 17 insertions(+), 21 deletions(-)
>
> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index 992396a..488678a 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
> @@ -1916,6 +1916,10 @@ retry:
>                 goto retry;
>         }
>
> +       if (!dev_validate_header(dev, skb->data, len)) {
> +               err = -EINVAL;
> +               goto out_unlock;
> +       }
>         if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
>             !packet_extra_vlan_len_allowed(dev, skb)) {
>                 err = -EMSGSIZE;
> @@ -2326,18 +2330,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
>         sock_wfree(skb);
>  }
>
> -static bool ll_header_truncated(const struct net_device *dev, int len)
> -{
> -       /* net device doesn't like empty head */
> -       if (unlikely(len < dev->hard_header_len)) {
> -               net_warn_ratelimited("%s: packet size is too short (%d < %d)\n",
> -                                    current->comm, len, dev->hard_header_len);
> -               return true;
> -       }
> -
> -       return false;
> -}
> -
>  static void tpacket_set_protocol(const struct net_device *dev,
>                                  struct sk_buff *skb)
>  {
> @@ -2420,14 +2412,14 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
>                 if (unlikely(err < 0))
>                         return -EINVAL;
>         } else if (dev->hard_header_len) {
> -               if (ll_header_truncated(dev, tp_len))
> -                       return -EINVAL;
> +               int hhlen = min_t(int, dev->hard_header_len, tp_len);
>
>                 skb_push(skb, dev->hard_header_len);
> -               err = skb_store_bits(skb, 0, data,
> -                               dev->hard_header_len);
> +               err = skb_store_bits(skb, 0, data, hhlen);
>                 if (unlikely(err))
>                         return err;
> +               if (!dev_validate_header(dev, skb->data, hhlen))
> +                       return -EINVAL;
>                 if (!skb->protocol)
>                         tpacket_set_protocol(dev, skb);
>
> @@ -2758,14 +2750,12 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
>
>         skb_set_network_header(skb, reserve);
>
> -       err = -EINVAL;
>         if (sock->type == SOCK_DGRAM) {
>                 offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len);
> -               if (unlikely(offset < 0))
> -                       goto out_free;
> -       } else {
> -               if (ll_header_truncated(dev, len))
> +               if (unlikely(offset < 0)) {
> +                       err = -EINVAL;
>                         goto out_free;
> +               }
>         }
>
>         /* Returns -EFAULT on error */
> @@ -2773,6 +2763,12 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
>         if (err)
>                 goto out_free;
>
> +       if (sock->type == SOCK_RAW &&
> +           !dev_validate_header(dev, skb->data, len)) {
> +               err = -EINVAL;
> +               goto out_free;
> +       }
> +
>         sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
>
>         if (!gso_type && (len > dev->mtu + reserve + extra_len) &&
> --
> 2.7.0.rc3.207.g0ac5344
>

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

* Re: [PATCH net 0/3] validate variable length ll headers
  2016-03-04 20:44 [PATCH net 0/3] validate variable length ll headers Willem de Bruijn
                   ` (2 preceding siblings ...)
  2016-03-04 20:44 ` [PATCH net 3/3] ax25: add link layer header validation function Willem de Bruijn
@ 2016-03-09 20:54 ` David Miller
  3 siblings, 0 replies; 7+ messages in thread
From: David Miller @ 2016-03-09 20:54 UTC (permalink / raw)
  To: willemdebruijn.kernel
  Cc: netdev, alan, hessu, martin.blumenstingl, linux-hams, willemb

From: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Date: Fri,  4 Mar 2016 15:44:14 -0500

> From: Willem de Bruijn <willemb@google.com>
> 
> Allow device-specific validation of link layer headers. Existing
> checks drop all packets shorter than hard_header_len. For variable
> length protocols, such packets can be valid.
> 
> patch 1 adds header_ops.validate and dev_validate_header
> patch 2 replaces ll_header_truncated with dev_validate_header
> patch 3 implements the protocol specific callback for AX25
> 
> Tested with a temporary eth_header_validate function. The AX25
> code is compile-tested only at this point.

I'm not going to be able to send another pull request to Linus
before -final, so please respin this against net-next and I'll
queue it up for -stable.

You can add the missing Fixes: tags as well when you do this.

Thanks.

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

end of thread, other threads:[~2016-03-09 20:54 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-04 20:44 [PATCH net 0/3] validate variable length ll headers Willem de Bruijn
2016-03-04 20:44 ` [PATCH net 1/3] net: " Willem de Bruijn
2016-03-05  9:22   ` walter harms
2016-03-04 20:44 ` [PATCH net 2/3] packet: " Willem de Bruijn
2016-03-07 17:38   ` Willem de Bruijn
2016-03-04 20:44 ` [PATCH net 3/3] ax25: add link layer header validation function Willem de Bruijn
2016-03-09 20:54 ` [PATCH net 0/3] validate variable length ll headers David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.