Commit d14a489a authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarEric Dumazet <edumazet@google.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Grégoire Baron <baronchon@n7mm.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3619eb85
...@@ -166,15 +166,17 @@ static int tcf_csum_ipv4_igmp(struct sk_buff *skb, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph, ...@@ -276,10 +284,11 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph,
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, ...@@ -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 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h, ...@@ -328,7 +338,7 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h,
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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment