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: 7f8a436eaa
("openvswitch: Add conntrack action")
Reported-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
c2229fe143
commit
74c1661813
3 changed files with 16 additions and 6 deletions
|
@ -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)
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
&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,
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue