diff mbox series

[SRU,Focal,1/1] esp: Fix possible buffer overflow in ESP transformation

Message ID 20220324111455.389344-3-cascardo@canonical.com
State New
Headers show
Series CVE-2022-27666 | expand

Commit Message

Thadeu Lima de Souza Cascardo March 24, 2022, 11:14 a.m. UTC
From: Steffen Klassert <steffen.klassert@secunet.com>

The maximum message size that can be send is bigger than
the  maximum site that skb_page_frag_refill can allocate.
So it is possible to write beyond the allocated buffer.

Fix this by doing a fallback to COW in that case.

v2:

Avoid get get_order() costs as suggested by Linus Torvalds.

Fixes: cac2661c53f3 ("esp4: Avoid skb_cow_data whenever possible")
Fixes: 03e2a30f6a27 ("esp6: Avoid skb_cow_data whenever possible")
Reported-by: valis <sec@valis.email>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
(backported from commit ebe48d368e97d007bfeb76fcb065d6cfc4c96645)
[cascardo: there was not x->encap test on esp6_output_head because of missing commit
 0146dca70b877b73c5fd9c67912b8a0ca8a7bac7]
[cascardo: add SKB_FRAG_PAGE_ORDER to include/net/sock.h]
CVE-2022-27666
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
---
 include/net/esp.h  | 2 ++
 include/net/sock.h | 1 +
 net/ipv4/esp4.c    | 5 +++++
 net/ipv6/esp6.c    | 5 +++++
 4 files changed, 13 insertions(+)

Comments

Stefan Bader March 24, 2022, 3:19 p.m. UTC | #1
On 24.03.22 12:14, Thadeu Lima de Souza Cascardo wrote:
> From: Steffen Klassert <steffen.klassert@secunet.com>
> 
> The maximum message size that can be send is bigger than
> the  maximum site that skb_page_frag_refill can allocate.
> So it is possible to write beyond the allocated buffer.
> 
> Fix this by doing a fallback to COW in that case.
> 
> v2:
> 
> Avoid get get_order() costs as suggested by Linus Torvalds.
> 
> Fixes: cac2661c53f3 ("esp4: Avoid skb_cow_data whenever possible")
> Fixes: 03e2a30f6a27 ("esp6: Avoid skb_cow_data whenever possible")
> Reported-by: valis <sec@valis.email>
> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
> (backported from commit ebe48d368e97d007bfeb76fcb065d6cfc4c96645)
> [cascardo: there was not x->encap test on esp6_output_head because of missing commit
>   0146dca70b877b73c5fd9c67912b8a0ca8a7bac7]
> [cascardo: add SKB_FRAG_PAGE_ORDER to include/net/sock.h]
> CVE-2022-27666
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
> ---

Applied to focal:linux/master-next. Thanks.

-Stefan

>   include/net/esp.h  | 2 ++
>   include/net/sock.h | 1 +
>   net/ipv4/esp4.c    | 5 +++++
>   net/ipv6/esp6.c    | 5 +++++
>   4 files changed, 13 insertions(+)
> 
> diff --git a/include/net/esp.h b/include/net/esp.h
> index 117652eb6ea3..465e38890ee9 100644
> --- a/include/net/esp.h
> +++ b/include/net/esp.h
> @@ -4,6 +4,8 @@
>   
>   #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/include/net/sock.h b/include/net/sock.h
> index 079b5f6f13d8..613f56c22a18 100644
> --- a/include/net/sock.h
> +++ b/include/net/sock.h
> @@ -2583,6 +2583,7 @@ extern int sysctl_optmem_max;
>   extern __u32 sysctl_wmem_default;
>   extern __u32 sysctl_rmem_default;
>   
> +#define SKB_FRAG_PAGE_ORDER	get_order(32768)
>   DECLARE_STATIC_KEY_FALSE(net_high_order_alloc_disable_key);
>   
>   static inline int sk_get_wmem0(const struct sock *sk, const struct proto *proto)
> diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
> index 86c836fa2145..94e17790769b 100644
> --- a/net/ipv4/esp4.c
> +++ b/net/ipv4/esp4.c
> @@ -277,6 +277,7 @@ 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 UDP Encapsulation */
>   	if (x->encap) {
> @@ -286,6 +287,10 @@ 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)
> +		goto cow;
> +
>   	if (!skb_cloned(skb)) {
>   		if (tailen <= skb_tailroom(skb)) {
>   			nfrags = 1;
> diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
> index 12570a73def8..06a172aa18c6 100644
> --- a/net/ipv6/esp6.c
> +++ b/net/ipv6/esp6.c
> @@ -230,6 +230,11 @@ 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;
> +
> +	allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES);
> +	if (allocsz > ESP_SKB_FRAG_MAXSIZE)
> +		goto cow;
>   
>   	if (!skb_cloned(skb)) {
>   		if (tailen <= skb_tailroom(skb)) {
diff mbox series

Patch

diff --git a/include/net/esp.h b/include/net/esp.h
index 117652eb6ea3..465e38890ee9 100644
--- a/include/net/esp.h
+++ b/include/net/esp.h
@@ -4,6 +4,8 @@ 
 
 #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/include/net/sock.h b/include/net/sock.h
index 079b5f6f13d8..613f56c22a18 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -2583,6 +2583,7 @@  extern int sysctl_optmem_max;
 extern __u32 sysctl_wmem_default;
 extern __u32 sysctl_rmem_default;
 
+#define SKB_FRAG_PAGE_ORDER	get_order(32768)
 DECLARE_STATIC_KEY_FALSE(net_high_order_alloc_disable_key);
 
 static inline int sk_get_wmem0(const struct sock *sk, const struct proto *proto)
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 86c836fa2145..94e17790769b 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -277,6 +277,7 @@  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 UDP Encapsulation */
 	if (x->encap) {
@@ -286,6 +287,10 @@  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)
+		goto cow;
+
 	if (!skb_cloned(skb)) {
 		if (tailen <= skb_tailroom(skb)) {
 			nfrags = 1;
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 12570a73def8..06a172aa18c6 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -230,6 +230,11 @@  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;
+
+	allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES);
+	if (allocsz > ESP_SKB_FRAG_MAXSIZE)
+		goto cow;
 
 	if (!skb_cloned(skb)) {
 		if (tailen <= skb_tailroom(skb)) {