Patchwork [Hardy,CVE-2012-2136] net: sock: validate data_len before allocating skb in sock_alloc_send_pskb()

login
register
mail settings
Submitter Tim Gardner
Date Sept. 7, 2012, 6:02 p.m.
Message ID <1347040953-13958-1-git-send-email-tim.gardner@canonical.com>
Download mbox | patch
Permalink /patch/182430/
State New
Headers show

Comments

Tim Gardner - Sept. 7, 2012, 6:02 p.m.
From: Jason Wang <jasowang@redhat.com>

CVE-2012-2136

BugLink: http://bugs.launchpad.net/bugs/1006622

We need to validate the number of pages consumed by data_len, otherwise frags
array could be overflowed by userspace. So this patch validate data_len and
return -EMSGSIZE when data_len may occupies more frags than MAX_SKB_FRAGS.

Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit cc9b17ad29ecaa20bfe426a8d4dbfb94b13ff1cc)

Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
---
 debian/binary-custom.d/openvz/src/net/core/sock.c |    5 +++++
 debian/binary-custom.d/xen/src/net/core/sock.c    |    7 +++++--
 net/core/sock.c                                   |    7 +++++--
 3 files changed, 15 insertions(+), 4 deletions(-)
Colin King - Sept. 10, 2012, 8:32 a.m.
On 07/09/12 19:02, Tim Gardner wrote:
> From: Jason Wang <jasowang@redhat.com>
>
> CVE-2012-2136
>
> BugLink: http://bugs.launchpad.net/bugs/1006622
>
> We need to validate the number of pages consumed by data_len, otherwise frags
> array could be overflowed by userspace. So this patch validate data_len and
> return -EMSGSIZE when data_len may occupies more frags than MAX_SKB_FRAGS.
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> Signed-off-by: David S. Miller <davem@davemloft.net>
> (cherry picked from commit cc9b17ad29ecaa20bfe426a8d4dbfb94b13ff1cc)
>

Minor quibble, this is also a back-port for the openvz version of sock.c 
rather than a clean cherry-pick.

> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
> ---
>   debian/binary-custom.d/openvz/src/net/core/sock.c |    5 +++++
>   debian/binary-custom.d/xen/src/net/core/sock.c    |    7 +++++--
>   net/core/sock.c                                   |    7 +++++--
>   3 files changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/debian/binary-custom.d/openvz/src/net/core/sock.c b/debian/binary-custom.d/openvz/src/net/core/sock.c
> index b66126b..46f0afc 100644
> --- a/debian/binary-custom.d/openvz/src/net/core/sock.c
> +++ b/debian/binary-custom.d/openvz/src/net/core/sock.c
> @@ -1267,6 +1267,11 @@ struct sk_buff *sock_alloc_send_skb2(struct sock *sk, unsigned long size,
>   	gfp_t gfp_mask;
>   	long timeo;
>   	int err;
> +	int npages = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
> +
> +	err = -EMSGSIZE;
> +	if (npages > MAX_SKB_FRAGS)
> +		goto failure;
>
>   	gfp_mask = sk->sk_allocation;
>   	if (gfp_mask & __GFP_WAIT)
> diff --git a/debian/binary-custom.d/xen/src/net/core/sock.c b/debian/binary-custom.d/xen/src/net/core/sock.c
> index b0e5208..a39b6aa 100644
> --- a/debian/binary-custom.d/xen/src/net/core/sock.c
> +++ b/debian/binary-custom.d/xen/src/net/core/sock.c
> @@ -1233,6 +1233,11 @@ static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
>   	gfp_t gfp_mask;
>   	long timeo;
>   	int err;
> +	int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
> +
> +	err = -EMSGSIZE;
> +	if (npages > MAX_SKB_FRAGS)
> +		goto failure;
>
>   	gfp_mask = sk->sk_allocation;
>   	if (gfp_mask & __GFP_WAIT)
> @@ -1251,14 +1256,12 @@ static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
>   		if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
>   			skb = alloc_skb(header_len, gfp_mask);
>   			if (skb) {
> -				int npages;
>   				int i;
>
>   				/* No pages, we're done... */
>   				if (!data_len)
>   					break;
>
> -				npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
>   				skb->truesize += data_len;
>   				skb_shinfo(skb)->nr_frags = npages;
>   				for (i = 0; i < npages; i++) {
> diff --git a/net/core/sock.c b/net/core/sock.c
> index b0e5208..a39b6aa 100644
> --- a/net/core/sock.c
> +++ b/net/core/sock.c
> @@ -1233,6 +1233,11 @@ static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
>   	gfp_t gfp_mask;
>   	long timeo;
>   	int err;
> +	int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
> +
> +	err = -EMSGSIZE;
> +	if (npages > MAX_SKB_FRAGS)
> +		goto failure;
>
>   	gfp_mask = sk->sk_allocation;
>   	if (gfp_mask & __GFP_WAIT)
> @@ -1251,14 +1256,12 @@ static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
>   		if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
>   			skb = alloc_skb(header_len, gfp_mask);
>   			if (skb) {
> -				int npages;
>   				int i;
>
>   				/* No pages, we're done... */
>   				if (!data_len)
>   					break;
>
> -				npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
>   				skb->truesize += data_len;
>   				skb_shinfo(skb)->nr_frags = npages;
>   				for (i = 0; i < npages; i++) {
>

Looks OK to me.

Acked-by: Colin Ian King <colin.king@canonical.com>
Tim Gardner - Sept. 10, 2012, 1:03 p.m.
On 09/10/2012 02:32 AM, Colin Ian King wrote:
> On 07/09/12 19:02, Tim Gardner wrote:
>> From: Jason Wang <jasowang@redhat.com>
>>
>> CVE-2012-2136
>>
>> BugLink: http://bugs.launchpad.net/bugs/1006622
>>
>> We need to validate the number of pages consumed by data_len,
>> otherwise frags
>> array could be overflowed by userspace. So this patch validate
>> data_len and
>> return -EMSGSIZE when data_len may occupies more frags than
>> MAX_SKB_FRAGS.
>>
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
>> Signed-off-by: David S. Miller <davem@davemloft.net>
>> (cherry picked from commit cc9b17ad29ecaa20bfe426a8d4dbfb94b13ff1cc)
>>
> 
> Minor quibble, this is also a back-port for the openvz version of sock.c
> rather than a clean cherry-pick.
> 

I guess I made the assumption that anyone doing maintenance on Hardy
would know that the custom binary patches _couldn't_ be cherry-picks.
But you are correct that I could have noted xen applied cleanly whereas
openvz required some futzing (as usual). I'll get that info into the
final patch.

rtg
Herton Ronaldo Krzesinski - Sept. 10, 2012, 1:34 p.m.

Tim Gardner - Sept. 10, 2012, 1:52 p.m.

Patch

diff --git a/debian/binary-custom.d/openvz/src/net/core/sock.c b/debian/binary-custom.d/openvz/src/net/core/sock.c
index b66126b..46f0afc 100644
--- a/debian/binary-custom.d/openvz/src/net/core/sock.c
+++ b/debian/binary-custom.d/openvz/src/net/core/sock.c
@@ -1267,6 +1267,11 @@  struct sk_buff *sock_alloc_send_skb2(struct sock *sk, unsigned long size,
 	gfp_t gfp_mask;
 	long timeo;
 	int err;
+	int npages = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+	err = -EMSGSIZE;
+	if (npages > MAX_SKB_FRAGS)
+		goto failure;
 
 	gfp_mask = sk->sk_allocation;
 	if (gfp_mask & __GFP_WAIT)
diff --git a/debian/binary-custom.d/xen/src/net/core/sock.c b/debian/binary-custom.d/xen/src/net/core/sock.c
index b0e5208..a39b6aa 100644
--- a/debian/binary-custom.d/xen/src/net/core/sock.c
+++ b/debian/binary-custom.d/xen/src/net/core/sock.c
@@ -1233,6 +1233,11 @@  static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
 	gfp_t gfp_mask;
 	long timeo;
 	int err;
+	int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+	err = -EMSGSIZE;
+	if (npages > MAX_SKB_FRAGS)
+		goto failure;
 
 	gfp_mask = sk->sk_allocation;
 	if (gfp_mask & __GFP_WAIT)
@@ -1251,14 +1256,12 @@  static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
 		if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
 			skb = alloc_skb(header_len, gfp_mask);
 			if (skb) {
-				int npages;
 				int i;
 
 				/* No pages, we're done... */
 				if (!data_len)
 					break;
 
-				npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 				skb->truesize += data_len;
 				skb_shinfo(skb)->nr_frags = npages;
 				for (i = 0; i < npages; i++) {
diff --git a/net/core/sock.c b/net/core/sock.c
index b0e5208..a39b6aa 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1233,6 +1233,11 @@  static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
 	gfp_t gfp_mask;
 	long timeo;
 	int err;
+	int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+	err = -EMSGSIZE;
+	if (npages > MAX_SKB_FRAGS)
+		goto failure;
 
 	gfp_mask = sk->sk_allocation;
 	if (gfp_mask & __GFP_WAIT)
@@ -1251,14 +1256,12 @@  static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
 		if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
 			skb = alloc_skb(header_len, gfp_mask);
 			if (skb) {
-				int npages;
 				int i;
 
 				/* No pages, we're done... */
 				if (!data_len)
 					break;
 
-				npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 				skb->truesize += data_len;
 				skb_shinfo(skb)->nr_frags = npages;
 				for (i = 0; i < npages; i++) {