| From 5747a76de308172a620ced427b8b9c160e17ad15 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 13 Apr 2022 10:10:50 +0200 |
| Subject: esp: limit skb_page_frag_refill use to a single page |
| |
| From: Sabrina Dubroca <sd@queasysnail.net> |
| |
| [ Upstream commit 5bd8baab087dff657e05387aee802e70304cc813 ] |
| |
| Commit ebe48d368e97 ("esp: Fix possible buffer overflow in ESP |
| transformation") tried to fix skb_page_frag_refill usage in ESP by |
| capping allocsize to 32k, but that doesn't completely solve the issue, |
| as skb_page_frag_refill may return a single page. If that happens, we |
| will write out of bounds, despite the check introduced in the previous |
| patch. |
| |
| This patch forces COW in cases where we would end up calling |
| skb_page_frag_refill with a size larger than a page (first in |
| esp_output_head with tailen, then in esp_output_tail with |
| skb->data_len). |
| |
| Fixes: cac2661c53f3 ("esp4: Avoid skb_cow_data whenever possible") |
| Fixes: 03e2a30f6a27 ("esp6: Avoid skb_cow_data whenever possible") |
| Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> |
| Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/net/esp.h | 2 -- |
| net/ipv4/esp4.c | 5 ++--- |
| net/ipv6/esp6.c | 5 ++--- |
| 3 files changed, 4 insertions(+), 8 deletions(-) |
| |
| diff --git a/include/net/esp.h b/include/net/esp.h |
| index 90cd02ff77ef..9c5637d41d95 100644 |
| --- a/include/net/esp.h |
| +++ b/include/net/esp.h |
| @@ -4,8 +4,6 @@ |
| |
| #include <linux/skbuff.h> |
| |
| -#define ESP_SKB_FRAG_MAXSIZE (PAGE_SIZE << SKB_FRAG_PAGE_ORDER) |
| - |
| struct ip_esp_hdr; |
| |
| static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb) |
| diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c |
| index 9aae82145bc1..20d738137841 100644 |
| --- a/net/ipv4/esp4.c |
| +++ b/net/ipv4/esp4.c |
| @@ -448,7 +448,6 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * |
| struct page *page; |
| struct sk_buff *trailer; |
| int tailen = esp->tailen; |
| - unsigned int allocsz; |
| |
| /* this is non-NULL only with TCP/UDP Encapsulation */ |
| if (x->encap) { |
| @@ -458,8 +457,8 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * |
| return err; |
| } |
| |
| - allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES); |
| - if (allocsz > ESP_SKB_FRAG_MAXSIZE) |
| + if (ALIGN(tailen, L1_CACHE_BYTES) > PAGE_SIZE || |
| + ALIGN(skb->data_len, L1_CACHE_BYTES) > PAGE_SIZE) |
| goto cow; |
| |
| if (!skb_cloned(skb)) { |
| diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c |
| index 20c7bef6829e..cb28f8928f9e 100644 |
| --- a/net/ipv6/esp6.c |
| +++ b/net/ipv6/esp6.c |
| @@ -483,7 +483,6 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info |
| struct page *page; |
| struct sk_buff *trailer; |
| int tailen = esp->tailen; |
| - unsigned int allocsz; |
| |
| if (x->encap) { |
| int err = esp6_output_encap(x, skb, esp); |
| @@ -492,8 +491,8 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info |
| return err; |
| } |
| |
| - allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES); |
| - if (allocsz > ESP_SKB_FRAG_MAXSIZE) |
| + if (ALIGN(tailen, L1_CACHE_BYTES) > PAGE_SIZE || |
| + ALIGN(skb->data_len, L1_CACHE_BYTES) > PAGE_SIZE) |
| goto cow; |
| |
| if (!skb_cloned(skb)) { |
| -- |
| 2.35.1 |
| |