diff mbox series

[net,v3] drivers/net/wan/lapbether: Use needed_headroom instead of hard_header_len

Message ID 20200802195046.402539-1-xie.he.0141@gmail.com
State Changes Requested
Delegated to: David Miller
Headers show
Series [net,v3] drivers/net/wan/lapbether: Use needed_headroom instead of hard_header_len | expand

Commit Message

Xie He Aug. 2, 2020, 7:50 p.m. UTC
In net/packet/af_packet.c, the function packet_snd first reserves a
headroom of length (dev->hard_header_len + dev->needed_headroom).
Then if the socket is a SOCK_DGRAM socket, it calls dev_hard_header,
which calls dev->header_ops->create, to create the link layer header.
If the socket is a SOCK_RAW socket, it "un-reserves" a headroom of
length (dev->hard_header_len), and assumes the user to provide the
appropriate link layer header.

So according to the logic of af_packet.c, dev->hard_header_len should
be the length of the header that would be created by
dev->header_ops->create.

However, this driver doesn't provide dev->header_ops, so logically
dev->hard_header_len should be 0.

So we should use dev->needed_headroom instead of dev->hard_header_len
to request necessary headroom to be allocated.

This change fixes kernel panic when this driver is used with AF_PACKET
SOCK_RAW sockets. Call stack when panic:

[  168.399197] skbuff: skb_under_panic: text:ffffffff819d95fb len:20
put:14 head:ffff8882704c0a00 data:ffff8882704c09fd tail:0x11 end:0xc0
dev:veth0
...
[  168.399255] Call Trace:
[  168.399259]  skb_push.cold+0x14/0x24
[  168.399262]  eth_header+0x2b/0xc0
[  168.399267]  lapbeth_data_transmit+0x9a/0xb0 [lapbether]
[  168.399275]  lapb_data_transmit+0x22/0x2c [lapb]
[  168.399277]  lapb_transmit_buffer+0x71/0xb0 [lapb]
[  168.399279]  lapb_kick+0xe3/0x1c0 [lapb]
[  168.399281]  lapb_data_request+0x76/0xc0 [lapb]
[  168.399283]  lapbeth_xmit+0x56/0x90 [lapbether]
[  168.399286]  dev_hard_start_xmit+0x91/0x1f0
[  168.399289]  ? irq_init_percpu_irqstack+0xc0/0x100
[  168.399291]  __dev_queue_xmit+0x721/0x8e0
[  168.399295]  ? packet_parse_headers.isra.0+0xd2/0x110
[  168.399297]  dev_queue_xmit+0x10/0x20
[  168.399298]  packet_sendmsg+0xbf0/0x19b0
......

Additional change:
When sending, check skb->len to ensure the 1-byte pseudo header is
present before reading it.

Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Cc: Brian Norris <briannorris@chromium.org>
Signed-off-by: Xie He <xie.he.0141@gmail.com>
---

Change from v2:
Added skb->len check when sending.

Change from v1:
None

---
 drivers/net/wan/lapbether.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

Comments

Willem de Bruijn Aug. 3, 2020, 9:49 a.m. UTC | #1
On Sun, Aug 2, 2020 at 9:51 PM Xie He <xie.he.0141@gmail.com> wrote:
>
> In net/packet/af_packet.c, the function packet_snd first reserves a
> headroom of length (dev->hard_header_len + dev->needed_headroom).
> Then if the socket is a SOCK_DGRAM socket, it calls dev_hard_header,
> which calls dev->header_ops->create, to create the link layer header.
> If the socket is a SOCK_RAW socket, it "un-reserves" a headroom of
> length (dev->hard_header_len), and assumes the user to provide the
> appropriate link layer header.
>
> So according to the logic of af_packet.c, dev->hard_header_len should
> be the length of the header that would be created by
> dev->header_ops->create.
>
> However, this driver doesn't provide dev->header_ops, so logically
> dev->hard_header_len should be 0.
>
> So we should use dev->needed_headroom instead of dev->hard_header_len
> to request necessary headroom to be allocated.
>
> This change fixes kernel panic when this driver is used with AF_PACKET
> SOCK_RAW sockets. Call stack when panic:
>
> [  168.399197] skbuff: skb_under_panic: text:ffffffff819d95fb len:20
> put:14 head:ffff8882704c0a00 data:ffff8882704c09fd tail:0x11 end:0xc0
> dev:veth0
> ...
> [  168.399255] Call Trace:
> [  168.399259]  skb_push.cold+0x14/0x24
> [  168.399262]  eth_header+0x2b/0xc0
> [  168.399267]  lapbeth_data_transmit+0x9a/0xb0 [lapbether]
> [  168.399275]  lapb_data_transmit+0x22/0x2c [lapb]
> [  168.399277]  lapb_transmit_buffer+0x71/0xb0 [lapb]
> [  168.399279]  lapb_kick+0xe3/0x1c0 [lapb]
> [  168.399281]  lapb_data_request+0x76/0xc0 [lapb]
> [  168.399283]  lapbeth_xmit+0x56/0x90 [lapbether]
> [  168.399286]  dev_hard_start_xmit+0x91/0x1f0
> [  168.399289]  ? irq_init_percpu_irqstack+0xc0/0x100
> [  168.399291]  __dev_queue_xmit+0x721/0x8e0
> [  168.399295]  ? packet_parse_headers.isra.0+0xd2/0x110
> [  168.399297]  dev_queue_xmit+0x10/0x20
> [  168.399298]  packet_sendmsg+0xbf0/0x19b0
> ......
>
> Additional change:
> When sending, check skb->len to ensure the 1-byte pseudo header is
> present before reading it.
>
> Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
> Cc: Brian Norris <briannorris@chromium.org>
> Signed-off-by: Xie He <xie.he.0141@gmail.com>

Overall, looks great. A  few nits.

It's [PATCH net v3], not [net v3]

> diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
> index b2868433718f..8a3f7ba36f7e 100644
> --- a/drivers/net/wan/lapbether.c
> +++ b/drivers/net/wan/lapbether.c
> @@ -157,6 +157,9 @@ static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
>         if (!netif_running(dev))
>                 goto drop;
>
> +       if (skb->len < 1)
> +               goto drop;
> +

Might be worth a comment along the lines of: /* upper layers pass a
control byte. must validate pf_packet input */

>         switch (skb->data[0]) {
>         case X25_IFACE_DATA:
>                 break;
> @@ -305,6 +308,7 @@ static void lapbeth_setup(struct net_device *dev)
>         dev->netdev_ops      = &lapbeth_netdev_ops;
>         dev->needs_free_netdev = true;
>         dev->type            = ARPHRD_X25;
> +       dev->hard_header_len = 0;

Technically not needed. The struct is allocated with kvzalloc, z for
__GFP_ZERO. Fine to leave if intended as self-describing comment, of
course.

>         dev->mtu             = 1000;
>         dev->addr_len        = 0;
>  }
> @@ -331,7 +335,8 @@ static int lapbeth_new_device(struct net_device *dev)
>          * then this driver prepends a length field of 2 bytes,
>          * then the underlying Ethernet device prepends its own header.
>          */
> -       ndev->hard_header_len = -1 + 3 + 2 + dev->hard_header_len;
> +       ndev->needed_headroom = -1 + 3 + 2 + dev->hard_header_len
> +                                          + dev->needed_headroom;
>
>         lapbeth = netdev_priv(ndev);
>         lapbeth->axdev = ndev;
> --
> 2.25.1
>
Xie He Aug. 3, 2020, 5:25 p.m. UTC | #2
Thanks!

On Mon, Aug 3, 2020 at 2:50 AM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> It's [PATCH net v3], not [net v3]

Sorry. My mistake. I'll pay attention next time.

I'm currently thinking about changing the subject to reflect that we
added a "skb->len" check. Should I number the new patch as v1 or
continue to number it as v4?

> > +       if (skb->len < 1)
> > +               goto drop;
> > +
>
> Might be worth a comment along the lines of: /* upper layers pass a
> control byte. must validate pf_packet input */

OK. I'll add the comment before it to make its meaning clearer.

> > +       dev->hard_header_len = 0;
>
> Technically not needed. The struct is allocated with kvzalloc, z for
> __GFP_ZERO. Fine to leave if intended as self-describing comment, of
> course.

Thanks for pointing out! I think I can leave it as a self-describing comment.

Thanks!
Martin Schiller Aug. 4, 2020, 12:43 p.m. UTC | #3
On 2020-08-02 21:50, Xie He wrote:
> In net/packet/af_packet.c, the function packet_snd first reserves a
> headroom of length (dev->hard_header_len + dev->needed_headroom).
> Then if the socket is a SOCK_DGRAM socket, it calls dev_hard_header,
> which calls dev->header_ops->create, to create the link layer header.
> If the socket is a SOCK_RAW socket, it "un-reserves" a headroom of
> length (dev->hard_header_len), and assumes the user to provide the
> appropriate link layer header.
> 
> So according to the logic of af_packet.c, dev->hard_header_len should
> be the length of the header that would be created by
> dev->header_ops->create.
> 
> However, this driver doesn't provide dev->header_ops, so logically
> dev->hard_header_len should be 0.
> 
> So we should use dev->needed_headroom instead of dev->hard_header_len
> to request necessary headroom to be allocated.

I'm not an expert in the field, but after reading the commit message and
the previous comments, I'd say that makes sense.

> 
> This change fixes kernel panic when this driver is used with AF_PACKET
> SOCK_RAW sockets. Call stack when panic:
> 
> [  168.399197] skbuff: skb_under_panic: text:ffffffff819d95fb len:20
> put:14 head:ffff8882704c0a00 data:ffff8882704c09fd tail:0x11 end:0xc0
> dev:veth0
> ...
> [  168.399255] Call Trace:
> [  168.399259]  skb_push.cold+0x14/0x24
> [  168.399262]  eth_header+0x2b/0xc0
> [  168.399267]  lapbeth_data_transmit+0x9a/0xb0 [lapbether]
> [  168.399275]  lapb_data_transmit+0x22/0x2c [lapb]
> [  168.399277]  lapb_transmit_buffer+0x71/0xb0 [lapb]
> [  168.399279]  lapb_kick+0xe3/0x1c0 [lapb]
> [  168.399281]  lapb_data_request+0x76/0xc0 [lapb]
> [  168.399283]  lapbeth_xmit+0x56/0x90 [lapbether]
> [  168.399286]  dev_hard_start_xmit+0x91/0x1f0
> [  168.399289]  ? irq_init_percpu_irqstack+0xc0/0x100
> [  168.399291]  __dev_queue_xmit+0x721/0x8e0
> [  168.399295]  ? packet_parse_headers.isra.0+0xd2/0x110
> [  168.399297]  dev_queue_xmit+0x10/0x20
> [  168.399298]  packet_sendmsg+0xbf0/0x19b0
> ......

Shouldn't this kernel panic be intercepted by a skb_cow() before the
skb_push() in lapbeth_data_transmit()?

> 
> Additional change:
> When sending, check skb->len to ensure the 1-byte pseudo header is
> present before reading it.
> 
> Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
> Cc: Brian Norris <briannorris@chromium.org>
> Signed-off-by: Xie He <xie.he.0141@gmail.com>
> ---
> 
> Change from v2:
> Added skb->len check when sending.
> 
> Change from v1:
> None
> 
> ---
>  drivers/net/wan/lapbether.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
> index b2868433718f..8a3f7ba36f7e 100644
> --- a/drivers/net/wan/lapbether.c
> +++ b/drivers/net/wan/lapbether.c
> @@ -157,6 +157,9 @@ static netdev_tx_t lapbeth_xmit(struct sk_buff 
> *skb,
>  	if (!netif_running(dev))
>  		goto drop;
> 
> +	if (skb->len < 1)
> +		goto drop;
> +
>  	switch (skb->data[0]) {
>  	case X25_IFACE_DATA:
>  		break;
> @@ -305,6 +308,7 @@ static void lapbeth_setup(struct net_device *dev)
>  	dev->netdev_ops	     = &lapbeth_netdev_ops;
>  	dev->needs_free_netdev = true;
>  	dev->type            = ARPHRD_X25;
> +	dev->hard_header_len = 0;
>  	dev->mtu             = 1000;
>  	dev->addr_len        = 0;
>  }
> @@ -331,7 +335,8 @@ static int lapbeth_new_device(struct net_device 
> *dev)
>  	 * then this driver prepends a length field of 2 bytes,
>  	 * then the underlying Ethernet device prepends its own header.
>  	 */
> -	ndev->hard_header_len = -1 + 3 + 2 + dev->hard_header_len;
> +	ndev->needed_headroom = -1 + 3 + 2 + dev->hard_header_len
> +					   + dev->needed_headroom;
> 
>  	lapbeth = netdev_priv(ndev);
>  	lapbeth->axdev = ndev;
Xie He Aug. 4, 2020, 7:20 p.m. UTC | #4
On Tue, Aug 4, 2020 at 5:43 AM Martin Schiller <ms@dev.tdt.de> wrote:
>
> I'm not an expert in the field, but after reading the commit message and
> the previous comments, I'd say that makes sense.

Thanks!

> Shouldn't this kernel panic be intercepted by a skb_cow() before the
> skb_push() in lapbeth_data_transmit()?

When a skb is passing down a protocol stack for transmission, there
might be several different skb_push calls to prepend different
headers. It would be the best (in terms of performance) if we can
allocate the needed header space in advance, so that we don't need to
reallocate the skb every time a new header needs to be prepended.
Adding skb_cow before these skb_push calls would indeed help
preventing kernel panics, but that might not be the essential issue
here, and it might also prevent us from discovering the real issue. (I
guess this is also the reason skb_cow is not included in skb_push
itself.)
Martin Schiller Aug. 5, 2020, 5:23 a.m. UTC | #5
On 2020-08-04 21:20, Xie He wrote:
> On Tue, Aug 4, 2020 at 5:43 AM Martin Schiller <ms@dev.tdt.de> wrote:
>> 
>> I'm not an expert in the field, but after reading the commit message 
>> and
>> the previous comments, I'd say that makes sense.
> 
> Thanks!
> 
>> Shouldn't this kernel panic be intercepted by a skb_cow() before the
>> skb_push() in lapbeth_data_transmit()?
> 
> When a skb is passing down a protocol stack for transmission, there
> might be several different skb_push calls to prepend different
> headers. It would be the best (in terms of performance) if we can
> allocate the needed header space in advance, so that we don't need to
> reallocate the skb every time a new header needs to be prepended.

Yes, I agree.

> Adding skb_cow before these skb_push calls would indeed help
> preventing kernel panics, but that might not be the essential issue
> here, and it might also prevent us from discovering the real issue. (I
> guess this is also the reason skb_cow is not included in skb_push
> itself.)

Well, you are right that the panic is "useful" to discover the real
problem. But on the other hand, if it is possible to prevent a panic, I
think we should do so. Maybe with adding a warning, when skb_cow() needs
to reallocate memory.

But this is getting a little bit off topic. For this patch I can say:

LGTM.

Reviewed-by: Martin Schiller <ms@dev.tdt.de>
Xie He Aug. 5, 2020, 8:57 a.m. UTC | #6
On Tue, Aug 4, 2020 at 10:23 PM Martin Schiller <ms@dev.tdt.de> wrote:
>
> > Adding skb_cow before these skb_push calls would indeed help
> > preventing kernel panics, but that might not be the essential issue
> > here, and it might also prevent us from discovering the real issue. (I
> > guess this is also the reason skb_cow is not included in skb_push
> > itself.)
>
> Well, you are right that the panic is "useful" to discover the real
> problem. But on the other hand, if it is possible to prevent a panic, I
> think we should do so. Maybe with adding a warning, when skb_cow() needs
> to reallocate memory.
>
> But this is getting a little bit off topic. For this patch I can say:
>
> LGTM.
>
> Reviewed-by: Martin Schiller <ms@dev.tdt.de>

Thank you so much!

Yes, it might be better to use skb_cow with a warning so that we can
prevent kernel panic while still being able to discover the problem.
If we want to do this, there are 2 more places in addition to
lapbeth_data_transmit that need to be guarded with skb_cow:
lapb_send_iframe and lapb_transmit_buffer in net/lapb/lapb_out.c.
Maybe we can address this in a separate patch.
Willem de Bruijn Aug. 5, 2020, 9:33 a.m. UTC | #7
On Wed, Aug 5, 2020 at 10:57 AM Xie He <xie.he.0141@gmail.com> wrote:
>
> On Tue, Aug 4, 2020 at 10:23 PM Martin Schiller <ms@dev.tdt.de> wrote:
> >
> > > Adding skb_cow before these skb_push calls would indeed help
> > > preventing kernel panics, but that might not be the essential issue
> > > here, and it might also prevent us from discovering the real issue. (I
> > > guess this is also the reason skb_cow is not included in skb_push
> > > itself.)
> >
> > Well, you are right that the panic is "useful" to discover the real
> > problem. But on the other hand, if it is possible to prevent a panic, I
> > think we should do so. Maybe with adding a warning, when skb_cow() needs
> > to reallocate memory.
> >
> > But this is getting a little bit off topic. For this patch I can say:
> >
> > LGTM.
> >
> > Reviewed-by: Martin Schiller <ms@dev.tdt.de>
>
> Thank you so much!
>
> Yes, it might be better to use skb_cow with a warning so that we can
> prevent kernel panic while still being able to discover the problem.

Let's not add defenses to work around possibly buggy code. In the long
run that reduces code quality. Instead, fix bugs at the source.
diff mbox series

Patch

diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
index b2868433718f..8a3f7ba36f7e 100644
--- a/drivers/net/wan/lapbether.c
+++ b/drivers/net/wan/lapbether.c
@@ -157,6 +157,9 @@  static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
 	if (!netif_running(dev))
 		goto drop;
 
+	if (skb->len < 1)
+		goto drop;
+
 	switch (skb->data[0]) {
 	case X25_IFACE_DATA:
 		break;
@@ -305,6 +308,7 @@  static void lapbeth_setup(struct net_device *dev)
 	dev->netdev_ops	     = &lapbeth_netdev_ops;
 	dev->needs_free_netdev = true;
 	dev->type            = ARPHRD_X25;
+	dev->hard_header_len = 0;
 	dev->mtu             = 1000;
 	dev->addr_len        = 0;
 }
@@ -331,7 +335,8 @@  static int lapbeth_new_device(struct net_device *dev)
 	 * then this driver prepends a length field of 2 bytes,
 	 * then the underlying Ethernet device prepends its own header.
 	 */
-	ndev->hard_header_len = -1 + 3 + 2 + dev->hard_header_len;
+	ndev->needed_headroom = -1 + 3 + 2 + dev->hard_header_len
+					   + dev->needed_headroom;
 
 	lapbeth = netdev_priv(ndev);
 	lapbeth->axdev = ndev;