[Openswan dev] [PATCH] Openswan: fix ipcomp skb offset calculations

Florian Westphal fwestphal at astaro.com
Mon Dec 1 03:50:01 EST 2008


When a compressed packed is received, the kernel panics
(also see http://bugs.xelerance.com/view.php?id=982 , which appears to describe
the same issue):

esi: c7158000   edi: c4066866   ebp: dfdbfa80   esp: dfdbfa58
ds: 007b   es: 007b   ss: 0068
Process swapper (pid: 0, threadinfo=dfdbe000 task=dfd9dac0)
Stack: <0>00000000 dfdbfb68 d2fc0938 00000006 c7158000 dfdbfb68 c4066866 d2f

dfdbfb68 c760b5e8 dfdbfabc e0a8a4e6 00000000 00000000 00000000 00000007
caaa9977 00000000 c71582ec 00007d14 c71582eb dfdbfb18 caaa9847 00000000
Call Trace:
 [<c010388d>] show_stack_log_lvl+0xab/0xb6
 [<c01039cc>] show_registers+0x134/0x1a8
 [<c0103ba4>] die+0x164/0x1e9
 [<c02bf650>] do_page_fault+0x377/0x528
 [<c01033fb>] error_code+0x4f/0x54
 [<e0a8a4e6>] ipcomp_inflate_codes+0x64e/0x658 [ipsec]
 [<e0a89ba4>] ipcomp_deflate_blocks+0x7bf/0xa53 [ipsec]
 [<e0a8abc0>] ipcomp_inflate+0x207/0x345 [ipsec]
 [<e0a87951>] skb_decompress+0x4f8/0x79c [ipsec]
 [<e0a7c66c>] ipsec_rcv_ipcomp_decomp+0x151/0x22d [ipsec]
 [<e0a6fa8a>] ipsec_rcv_decap_once+0x7e1/0xc41 [ipsec]
 [<e0a6ff4b>] ipsec_rcv_decap+0x61/0x920 [ipsec]
 [<e0a70e57>] ipsec_rcv+0x4ef/0x544 [ipsec]
 [<c0284ae8>] ip_local_deliver+0x161/0x229
 [<c028490e>] ip_rcv+0x401/0x47a
 [<c026dbd9>] netif_receive_skb+0x383/0x418
 [<c026f1b6>] process_backlog+0x8a/0xf5
 [<c026f28f>] net_rx_action+0x6e/0x108
 [<c011c47a>] __do_softirq+0x60/0xce
 [<c011c519>] do_softirq+0x31/0x36
 [<c011c688>] irq_exit+0x34/0x36
 [<c01046c2>] do_IRQ+0x4e/0x5a
 [<c01032a2>] common_interrupt+0x1a/0x20
 [<c0101984>] cpu_idle+0x5c/0x71
 [<c010c9bb>] start_secondary+0x3c1/0x3c9
 [<00000000>] _stext+0x3feffd68/0x2d

We observe this with 2.4.13, but this appears to be present
in 2.6.19 as well.

It is due to bogus offset calculations in ipcomp.c:skb_copy_ipcomp().
"offset" is the difference between two skbs, yet the value is used to
set the skb protocol header fields.

offset=n->head-skb->head;
n->nh.raw=skb->nh.raw+offset;

got changed to

offset=n->head-skb->head;
skb_set_network_header(n, offset);

which will make the network header point to somewhere
outside the skbs allocated memory area,
causing the kernel to OOOPS on the first receipt of a compressed packet.

This makes things work again for us with 2.4.13 on a Linux 2.6.16-based kernel,
it would be great if you could double-check this because we don't use all
the code paths there due to #ifdefs.

Index: linux-2.6.16.62/net/ipsec/ipcomp.c
===================================================================
--- linux-2.6.16.62.orig/net/ipsec/ipcomp.c	2008-11-24 09:33:25.000000000 +0100
+++ linux-2.6.16.62/net/ipsec/ipcomp.c	2008-11-24 10:52:44.000000000 +0100
@@ -583,9 +583,9 @@ struct sk_buff *skb_copy_ipcomp(struct s
 {
         struct sk_buff *n;
 	struct iphdr *iph;
-        unsigned long offset;
         unsigned int iphlen;
-	
+	int headlen;
+
 	if(!skb) {
 		KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
 			    "klips_debug:skb_copy_ipcomp: "
@@ -608,15 +608,10 @@ struct sk_buff *skb_copy_ipcomp(struct s
         n=alloc_skb(skb_end_pointer(skb) - skb->head + data_growth, gfp_mask);
         if(n==NULL)
                 return NULL;
-	
-        /*
-         *      Shift between the two data areas in bytes
-         */
-	
-        offset=n->head-skb->head;
 
         /* Set the data pointer */
-        skb_reserve(n,skb->data-skb->head);
+	headlen = skb_headroom(skb);
+	skb_reserve(n, headlen);
         /* Set the tail pointer and length */
         safe_skb_put(n,skb->len+data_growth);
         /* Copy the bytes up to and including the ip header */
@@ -630,14 +625,18 @@ struct sk_buff *skb_copy_ipcomp(struct s
 	n->prev=NULL;
         n->sk=NULL;
         n->dev=skb->dev;
-	if (skb_transport_header(skb))
-		skb_set_transport_header(n, offset);
+
+	if (skb_transport_header(skb)) {
+		headlen = skb->h.raw - skb->data;
+		skb_set_transport_header(n, headlen);
+	}
         n->protocol=skb->protocol;
 #ifdef NET_21
         n->csum = 0;
         n->priority=skb->priority;
         n->dst=dst_clone(skb->dst);
-        skb_set_network_header(n, offset);
+        headlen = skb->nh.raw - skb->data;
+        skb_set_network_header(n, headlen);
 #ifndef NETDEV_23
         n->is_clone=0;
 #endif /* NETDEV_23 */
@@ -653,7 +652,8 @@ struct sk_buff *skb_copy_ipcomp(struct s
 #else /* NET_21 */
 	n->link3=NULL;
 	n->when=skb->when;
-	n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
+	headlen = skb->ip_hdr - skb->data;
+	n->ip_hdr=(struct iphdr *) (((char *) n->data) + headlen);
 	n->saddr=skb->saddr;
 	n->daddr=skb->daddr;
 	n->raddr=skb->raddr;
@@ -668,8 +668,10 @@ struct sk_buff *skb_copy_ipcomp(struct s
 	n->users=0;
 	memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
 #endif /* NET_21 */
-	if (skb_mac_header(skb))
-		skb_set_mac_header(n, offset);
+	if (skb_mac_header(skb)) {
+		headlen = skb_mac_header(skb) - skb->data;
+		skb_set_mac_header(n, headlen);
+	}
 #ifndef NETDEV_23
 	n->used=skb->used;
 #endif /* !NETDEV_23 */
-- 
Florian Westphal <fwestphal at astaro.com> | Linux Kernel Programmer
Astaro AG | www.astaro.com | Phone 49-721-25516-0 | Fax -200
Amalienbadstrasse 36 / Bau 33a | 76227 Karlsruhe | Germany


More information about the Dev mailing list