act_csum: fix possible use after free

tcf_csum_skb_nextlayer() / pskb_may_pull() can change skb->head, so we
must be careful not keeping pointers to previous headers.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Grégoire Baron <baronchon@n7mm.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Dumazet 2013-04-12 11:07:47 -07:00 committed by David S. Miller
parent 3619eb8541
commit d14a489a41
1 changed files with 25 additions and 14 deletions

View File

@ -166,15 +166,17 @@ static int tcf_csum_ipv4_igmp(struct sk_buff *skb,
return 1; return 1;
} }
static int tcf_csum_ipv6_icmp(struct sk_buff *skb, struct ipv6hdr *ip6h, static int tcf_csum_ipv6_icmp(struct sk_buff *skb,
unsigned int ihl, unsigned int ipl) unsigned int ihl, unsigned int ipl)
{ {
struct icmp6hdr *icmp6h; struct icmp6hdr *icmp6h;
const struct ipv6hdr *ip6h;
icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h)); icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h));
if (icmp6h == NULL) if (icmp6h == NULL)
return 0; return 0;
ip6h = ipv6_hdr(skb);
icmp6h->icmp6_cksum = 0; icmp6h->icmp6_cksum = 0;
skb->csum = csum_partial(icmp6h, ipl - ihl, 0); skb->csum = csum_partial(icmp6h, ipl - ihl, 0);
icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
@ -186,15 +188,17 @@ static int tcf_csum_ipv6_icmp(struct sk_buff *skb, struct ipv6hdr *ip6h,
return 1; return 1;
} }
static int tcf_csum_ipv4_tcp(struct sk_buff *skb, struct iphdr *iph, static int tcf_csum_ipv4_tcp(struct sk_buff *skb,
unsigned int ihl, unsigned int ipl) unsigned int ihl, unsigned int ipl)
{ {
struct tcphdr *tcph; struct tcphdr *tcph;
const struct iphdr *iph;
tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
if (tcph == NULL) if (tcph == NULL)
return 0; return 0;
iph = ip_hdr(skb);
tcph->check = 0; tcph->check = 0;
skb->csum = csum_partial(tcph, ipl - ihl, 0); skb->csum = csum_partial(tcph, ipl - ihl, 0);
tcph->check = tcp_v4_check(ipl - ihl, tcph->check = tcp_v4_check(ipl - ihl,
@ -205,15 +209,17 @@ static int tcf_csum_ipv4_tcp(struct sk_buff *skb, struct iphdr *iph,
return 1; return 1;
} }
static int tcf_csum_ipv6_tcp(struct sk_buff *skb, struct ipv6hdr *ip6h, static int tcf_csum_ipv6_tcp(struct sk_buff *skb,
unsigned int ihl, unsigned int ipl) unsigned int ihl, unsigned int ipl)
{ {
struct tcphdr *tcph; struct tcphdr *tcph;
const struct ipv6hdr *ip6h;
tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
if (tcph == NULL) if (tcph == NULL)
return 0; return 0;
ip6h = ipv6_hdr(skb);
tcph->check = 0; tcph->check = 0;
skb->csum = csum_partial(tcph, ipl - ihl, 0); skb->csum = csum_partial(tcph, ipl - ihl, 0);
tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
@ -225,10 +231,11 @@ static int tcf_csum_ipv6_tcp(struct sk_buff *skb, struct ipv6hdr *ip6h,
return 1; return 1;
} }
static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph, static int tcf_csum_ipv4_udp(struct sk_buff *skb,
unsigned int ihl, unsigned int ipl, int udplite) unsigned int ihl, unsigned int ipl, int udplite)
{ {
struct udphdr *udph; struct udphdr *udph;
const struct iphdr *iph;
u16 ul; u16 ul;
/* /*
@ -242,6 +249,7 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph,
if (udph == NULL) if (udph == NULL)
return 0; return 0;
iph = ip_hdr(skb);
ul = ntohs(udph->len); ul = ntohs(udph->len);
if (udplite || udph->check) { if (udplite || udph->check) {
@ -276,10 +284,11 @@ ignore_obscure_skb:
return 1; return 1;
} }
static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h, static int tcf_csum_ipv6_udp(struct sk_buff *skb,
unsigned int ihl, unsigned int ipl, int udplite) unsigned int ihl, unsigned int ipl, int udplite)
{ {
struct udphdr *udph; struct udphdr *udph;
const struct ipv6hdr *ip6h;
u16 ul; u16 ul;
/* /*
@ -293,6 +302,7 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h,
if (udph == NULL) if (udph == NULL)
return 0; return 0;
ip6h = ipv6_hdr(skb);
ul = ntohs(udph->len); ul = ntohs(udph->len);
udph->check = 0; udph->check = 0;
@ -328,7 +338,7 @@ ignore_obscure_skb:
static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags) static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
{ {
struct iphdr *iph; const struct iphdr *iph;
int ntkoff; int ntkoff;
ntkoff = skb_network_offset(skb); ntkoff = skb_network_offset(skb);
@ -353,19 +363,19 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
break; break;
case IPPROTO_TCP: case IPPROTO_TCP:
if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
if (!tcf_csum_ipv4_tcp(skb, iph, iph->ihl * 4, if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4,
ntohs(iph->tot_len))) ntohs(iph->tot_len)))
goto fail; goto fail;
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
if (!tcf_csum_ipv4_udp(skb, iph, iph->ihl * 4, if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
ntohs(iph->tot_len), 0)) ntohs(iph->tot_len), 0))
goto fail; goto fail;
break; break;
case IPPROTO_UDPLITE: case IPPROTO_UDPLITE:
if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
if (!tcf_csum_ipv4_udp(skb, iph, iph->ihl * 4, if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
ntohs(iph->tot_len), 1)) ntohs(iph->tot_len), 1))
goto fail; goto fail;
break; break;
@ -377,7 +387,7 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto fail; goto fail;
ip_send_check(iph); ip_send_check(ip_hdr(skb));
} }
return 1; return 1;
@ -456,6 +466,7 @@ static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags)
ixhl = ipv6_optlen(ip6xh); ixhl = ipv6_optlen(ip6xh);
if (!pskb_may_pull(skb, hl + ixhl + ntkoff)) if (!pskb_may_pull(skb, hl + ixhl + ntkoff))
goto fail; goto fail;
ip6xh = (void *)(skb_network_header(skb) + hl);
if ((nexthdr == NEXTHDR_HOP) && if ((nexthdr == NEXTHDR_HOP) &&
!(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl))) !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl)))
goto fail; goto fail;
@ -464,25 +475,25 @@ static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags)
break; break;
case IPPROTO_ICMPV6: case IPPROTO_ICMPV6:
if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP)
if (!tcf_csum_ipv6_icmp(skb, ip6h, if (!tcf_csum_ipv6_icmp(skb,
hl, pl + sizeof(*ip6h))) hl, pl + sizeof(*ip6h)))
goto fail; goto fail;
goto done; goto done;
case IPPROTO_TCP: case IPPROTO_TCP:
if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
if (!tcf_csum_ipv6_tcp(skb, ip6h, if (!tcf_csum_ipv6_tcp(skb,
hl, pl + sizeof(*ip6h))) hl, pl + sizeof(*ip6h)))
goto fail; goto fail;
goto done; goto done;
case IPPROTO_UDP: case IPPROTO_UDP:
if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
if (!tcf_csum_ipv6_udp(skb, ip6h, hl, if (!tcf_csum_ipv6_udp(skb, hl,
pl + sizeof(*ip6h), 0)) pl + sizeof(*ip6h), 0))
goto fail; goto fail;
goto done; goto done;
case IPPROTO_UDPLITE: case IPPROTO_UDPLITE:
if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
if (!tcf_csum_ipv6_udp(skb, ip6h, hl, if (!tcf_csum_ipv6_udp(skb, hl,
pl + sizeof(*ip6h), 1)) pl + sizeof(*ip6h), 1))
goto fail; goto fail;
goto done; goto done;