aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Stringer <joestringer@nicira.com>2015-10-25 23:21:48 -0400
committerDavid S. Miller <davem@davemloft.net>2015-10-27 22:32:14 -0400
commit74c16618137f1505b0a32dea3ec73a2ef6f8f842 (patch)
treea85b6a44ec0f5d8d7ae915968ee88250c323a292
parentc2229fe1430d4e1c70e36520229dd64a87802b20 (diff)
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: 7f8a436eaa2c ("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>
-rw-r--r--net/openvswitch/actions.c4
-rw-r--r--net/openvswitch/conntrack.c17
-rw-r--r--net/openvswitch/conntrack.h1
3 files changed, 16 insertions, 6 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 0bf0f406de52..dba635d086b2 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -1109,8 +1109,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
1109 nla_data(a)); 1109 nla_data(a));
1110 1110
1111 /* Hide stolen IP fragments from user space. */ 1111 /* Hide stolen IP fragments from user space. */
1112 if (err == -EINPROGRESS) 1112 if (err)
1113 return 0; 1113 return err == -EINPROGRESS ? 0 : err;
1114 break; 1114 break;
1115 } 1115 }
1116 1116
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index a5ec34f8502f..b5dcc0abde66 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -293,6 +293,9 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
293 return helper->help(skb, protoff, ct, ctinfo); 293 return helper->help(skb, protoff, ct, ctinfo);
294} 294}
295 295
296/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
297 * value if 'skb' is freed.
298 */
296static int handle_fragments(struct net *net, struct sw_flow_key *key, 299static int handle_fragments(struct net *net, struct sw_flow_key *key,
297 u16 zone, struct sk_buff *skb) 300 u16 zone, struct sk_buff *skb)
298{ 301{
@@ -308,8 +311,8 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
308 return err; 311 return err;
309 312
310 ovs_cb.mru = IPCB(skb)->frag_max_size; 313 ovs_cb.mru = IPCB(skb)->frag_max_size;
311 } else if (key->eth.type == htons(ETH_P_IPV6)) {
312#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 314#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
315 } else if (key->eth.type == htons(ETH_P_IPV6)) {
313 enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone; 316 enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone;
314 struct sk_buff *reasm; 317 struct sk_buff *reasm;
315 318
@@ -318,17 +321,18 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
318 if (!reasm) 321 if (!reasm)
319 return -EINPROGRESS; 322 return -EINPROGRESS;
320 323
321 if (skb == reasm) 324 if (skb == reasm) {
325 kfree_skb(skb);
322 return -EINVAL; 326 return -EINVAL;
327 }
323 328
324 key->ip.proto = ipv6_hdr(reasm)->nexthdr; 329 key->ip.proto = ipv6_hdr(reasm)->nexthdr;
325 skb_morph(skb, reasm); 330 skb_morph(skb, reasm);
326 consume_skb(reasm); 331 consume_skb(reasm);
327 ovs_cb.mru = IP6CB(skb)->frag_max_size; 332 ovs_cb.mru = IP6CB(skb)->frag_max_size;
328#else
329 return -EPFNOSUPPORT;
330#endif 333#endif
331 } else { 334 } else {
335 kfree_skb(skb);
332 return -EPFNOSUPPORT; 336 return -EPFNOSUPPORT;
333 } 337 }
334 338
@@ -473,6 +477,9 @@ static bool labels_nonzero(const struct ovs_key_ct_labels *labels)
473 return false; 477 return false;
474} 478}
475 479
480/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
481 * value if 'skb' is freed.
482 */
476int ovs_ct_execute(struct net *net, struct sk_buff *skb, 483int ovs_ct_execute(struct net *net, struct sk_buff *skb,
477 struct sw_flow_key *key, 484 struct sw_flow_key *key,
478 const struct ovs_conntrack_info *info) 485 const struct ovs_conntrack_info *info)
@@ -508,6 +515,8 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
508 &info->labels.mask); 515 &info->labels.mask);
509err: 516err:
510 skb_push(skb, nh_ofs); 517 skb_push(skb, nh_ofs);
518 if (err)
519 kfree_skb(skb);
511 return err; 520 return err;
512} 521}
513 522
diff --git a/net/openvswitch/conntrack.h b/net/openvswitch/conntrack.h
index 82e0dfc66028..a7544f405c16 100644
--- a/net/openvswitch/conntrack.h
+++ b/net/openvswitch/conntrack.h
@@ -67,6 +67,7 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb,
67 struct sw_flow_key *key, 67 struct sw_flow_key *key,
68 const struct ovs_conntrack_info *info) 68 const struct ovs_conntrack_info *info)
69{ 69{
70 kfree_skb(skb);
70 return -ENOTSUPP; 71 return -ENOTSUPP;
71} 72}
72 73