Commit 74c16618 authored by Joe Stringer's avatar Joe Stringer Committed by David S. Miller

openvswitch: Fix double-free on ip_defrag() errors

If ip_defrag() returns an error other than -EINPROGRESS, then the skb is
freed. When handle_fragments() passes this back up to
do_execute_actions(), it will be freed again. Prevent this double free
by never freeing the skb in do_execute_actions() for errors returned by
ovs_ct_execute. Always free it in ovs_ct_execute() error paths instead.

Fixes: 7f8a436e ("openvswitch: Add conntrack action")
Reported-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarJoe Stringer <joestringer@nicira.com>
Acked-by: default avatarPravin B Shelar <pshelar@nicira.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c2229fe1
...@@ -1109,8 +1109,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, ...@@ -1109,8 +1109,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
nla_data(a)); nla_data(a));
/* Hide stolen IP fragments from user space. */ /* Hide stolen IP fragments from user space. */
if (err == -EINPROGRESS) if (err)
return 0; return err == -EINPROGRESS ? 0 : err;
break; break;
} }
......
...@@ -293,6 +293,9 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto) ...@@ -293,6 +293,9 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
return helper->help(skb, protoff, ct, ctinfo); return helper->help(skb, protoff, ct, ctinfo);
} }
/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
* value if 'skb' is freed.
*/
static int handle_fragments(struct net *net, struct sw_flow_key *key, static int handle_fragments(struct net *net, struct sw_flow_key *key,
u16 zone, struct sk_buff *skb) u16 zone, struct sk_buff *skb)
{ {
...@@ -308,8 +311,8 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key, ...@@ -308,8 +311,8 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
return err; return err;
ovs_cb.mru = IPCB(skb)->frag_max_size; ovs_cb.mru = IPCB(skb)->frag_max_size;
} else if (key->eth.type == htons(ETH_P_IPV6)) {
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
} else if (key->eth.type == htons(ETH_P_IPV6)) {
enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone; enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone;
struct sk_buff *reasm; struct sk_buff *reasm;
...@@ -318,17 +321,18 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key, ...@@ -318,17 +321,18 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
if (!reasm) if (!reasm)
return -EINPROGRESS; return -EINPROGRESS;
if (skb == reasm) if (skb == reasm) {
kfree_skb(skb);
return -EINVAL; return -EINVAL;
}
key->ip.proto = ipv6_hdr(reasm)->nexthdr; key->ip.proto = ipv6_hdr(reasm)->nexthdr;
skb_morph(skb, reasm); skb_morph(skb, reasm);
consume_skb(reasm); consume_skb(reasm);
ovs_cb.mru = IP6CB(skb)->frag_max_size; ovs_cb.mru = IP6CB(skb)->frag_max_size;
#else
return -EPFNOSUPPORT;
#endif #endif
} else { } else {
kfree_skb(skb);
return -EPFNOSUPPORT; return -EPFNOSUPPORT;
} }
...@@ -473,6 +477,9 @@ static bool labels_nonzero(const struct ovs_key_ct_labels *labels) ...@@ -473,6 +477,9 @@ static bool labels_nonzero(const struct ovs_key_ct_labels *labels)
return false; return false;
} }
/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
* value if 'skb' is freed.
*/
int ovs_ct_execute(struct net *net, struct sk_buff *skb, int ovs_ct_execute(struct net *net, struct sk_buff *skb,
struct sw_flow_key *key, struct sw_flow_key *key,
const struct ovs_conntrack_info *info) const struct ovs_conntrack_info *info)
...@@ -508,6 +515,8 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, ...@@ -508,6 +515,8 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
&info->labels.mask); &info->labels.mask);
err: err:
skb_push(skb, nh_ofs); skb_push(skb, nh_ofs);
if (err)
kfree_skb(skb);
return err; return err;
} }
......
...@@ -67,6 +67,7 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb, ...@@ -67,6 +67,7 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb,
struct sw_flow_key *key, struct sw_flow_key *key,
const struct ovs_conntrack_info *info) const struct ovs_conntrack_info *info)
{ {
kfree_skb(skb);
return -ENOTSUPP; return -ENOTSUPP;
} }
......
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