Skip to content

Commit

Permalink
openvswitch: Fix double-free on ip_defrag() errors
Browse files Browse the repository at this point in the history
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: 7f8a436 ("openvswitch: Add conntrack action")
Reported-by: Florian Westphal <[email protected]>
Signed-off-by: Joe Stringer <[email protected]>
Acked-by: Pravin B Shelar <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
joestringer authored and davem330 committed Oct 28, 2015
1 parent c2229fe commit 74c1661
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 6 deletions.
4 changes: 2 additions & 2 deletions net/openvswitch/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1109,8 +1109,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
nla_data(a));

/* Hide stolen IP fragments from user space. */
if (err == -EINPROGRESS)
return 0;
if (err)
return err == -EINPROGRESS ? 0 : err;
break;
}

Expand Down
17 changes: 13 additions & 4 deletions net/openvswitch/conntrack.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
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,
u16 zone, struct sk_buff *skb)
{
Expand All @@ -308,8 +311,8 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
return err;

ovs_cb.mru = IPCB(skb)->frag_max_size;
} else if (key->eth.type == htons(ETH_P_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;
struct sk_buff *reasm;

Expand All @@ -318,17 +321,18 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
if (!reasm)
return -EINPROGRESS;

if (skb == reasm)
if (skb == reasm) {
kfree_skb(skb);
return -EINVAL;
}

key->ip.proto = ipv6_hdr(reasm)->nexthdr;
skb_morph(skb, reasm);
consume_skb(reasm);
ovs_cb.mru = IP6CB(skb)->frag_max_size;
#else
return -EPFNOSUPPORT;
#endif
} else {
kfree_skb(skb);
return -EPFNOSUPPORT;
}

Expand Down Expand Up @@ -473,6 +477,9 @@ static bool labels_nonzero(const struct ovs_key_ct_labels *labels)
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,
struct sw_flow_key *key,
const struct ovs_conntrack_info *info)
Expand Down Expand Up @@ -508,6 +515,8 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
&info->labels.mask);
err:
skb_push(skb, nh_ofs);
if (err)
kfree_skb(skb);
return err;
}

Expand Down
1 change: 1 addition & 0 deletions net/openvswitch/conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb,
struct sw_flow_key *key,
const struct ovs_conntrack_info *info)
{
kfree_skb(skb);
return -ENOTSUPP;
}

Expand Down

0 comments on commit 74c1661

Please sign in to comment.