| From 38ec4944b593fd90c5ef42aaaa53e66ae5769d04 Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Tue, 13 Apr 2021 05:41:35 -0700 |
| Subject: gro: ensure frag0 meets IP header alignment |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| commit 38ec4944b593fd90c5ef42aaaa53e66ae5769d04 upstream. |
| |
| After commit 0f6925b3e8da ("virtio_net: Do not pull payload in skb->head") |
| Guenter Roeck reported one failure in his tests using sh architecture. |
| |
| After much debugging, we have been able to spot silent unaligned accesses |
| in inet_gro_receive() |
| |
| The issue at hand is that upper networking stacks assume their header |
| is word-aligned. Low level drivers are supposed to reserve NET_IP_ALIGN |
| bytes before the Ethernet header to make that happen. |
| |
| This patch hardens skb_gro_reset_offset() to not allow frag0 fast-path |
| if the fragment is not properly aligned. |
| |
| Some arches like x86, arm64 and powerpc do not care and define NET_IP_ALIGN |
| as 0, this extra check will be a NOP for them. |
| |
| Note that if frag0 is not used, GRO will call pskb_may_pull() |
| as many times as needed to pull network and transport headers. |
| |
| Fixes: 0f6925b3e8da ("virtio_net: Do not pull payload in skb->head") |
| Fixes: 78a478d0efd9 ("gro: Inline skb_gro_header and cache frag0 virtual address") |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Reported-by: Guenter Roeck <linux@roeck-us.net> |
| Cc: Xuan Zhuo <xuanzhuo@linux.alibaba.com> |
| Cc: "Michael S. Tsirkin" <mst@redhat.com> |
| Cc: Jason Wang <jasowang@redhat.com> |
| Acked-by: Michael S. Tsirkin <mst@redhat.com> |
| Tested-by: Guenter Roeck <linux@roeck-us.net> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/core/dev.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| --- a/net/core/dev.c |
| +++ b/net/core/dev.c |
| @@ -5877,7 +5877,8 @@ static void skb_gro_reset_offset(struct |
| NAPI_GRO_CB(skb)->frag0_len = 0; |
| |
| if (!skb_headlen(skb) && pinfo->nr_frags && |
| - !PageHighMem(skb_frag_page(frag0))) { |
| + !PageHighMem(skb_frag_page(frag0)) && |
| + (!NET_IP_ALIGN || !(skb_frag_off(frag0) & 3))) { |
| NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0); |
| NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int, |
| skb_frag_size(frag0), |