Skip to content

Commit 74c1661

Browse files
joestringerdavem330
authored andcommitted
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: 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]>
1 parent c2229fe commit 74c1661

File tree

3 files changed

+16
-6
lines changed

3 files changed

+16
-6
lines changed

net/openvswitch/actions.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,8 +1109,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
11091109
nla_data(a));
11101110

11111111
/* Hide stolen IP fragments from user space. */
1112-
if (err == -EINPROGRESS)
1113-
return 0;
1112+
if (err)
1113+
return err == -EINPROGRESS ? 0 : err;
11141114
break;
11151115
}
11161116

net/openvswitch/conntrack.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
293293
return helper->help(skb, protoff, ct, ctinfo);
294294
}
295295

296+
/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
297+
* value if 'skb' is freed.
298+
*/
296299
static int handle_fragments(struct net *net, struct sw_flow_key *key,
297300
u16 zone, struct sk_buff *skb)
298301
{
@@ -308,8 +311,8 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
308311
return err;
309312

310313
ovs_cb.mru = IPCB(skb)->frag_max_size;
311-
} else if (key->eth.type == htons(ETH_P_IPV6)) {
312314
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
315+
} else if (key->eth.type == htons(ETH_P_IPV6)) {
313316
enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone;
314317
struct sk_buff *reasm;
315318

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

321-
if (skb == reasm)
324+
if (skb == reasm) {
325+
kfree_skb(skb);
322326
return -EINVAL;
327+
}
323328

324329
key->ip.proto = ipv6_hdr(reasm)->nexthdr;
325330
skb_morph(skb, reasm);
326331
consume_skb(reasm);
327332
ovs_cb.mru = IP6CB(skb)->frag_max_size;
328-
#else
329-
return -EPFNOSUPPORT;
330333
#endif
331334
} else {
335+
kfree_skb(skb);
332336
return -EPFNOSUPPORT;
333337
}
334338

@@ -473,6 +477,9 @@ static bool labels_nonzero(const struct ovs_key_ct_labels *labels)
473477
return false;
474478
}
475479

480+
/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
481+
* value if 'skb' is freed.
482+
*/
476483
int ovs_ct_execute(struct net *net, struct sk_buff *skb,
477484
struct sw_flow_key *key,
478485
const struct ovs_conntrack_info *info)
@@ -508,6 +515,8 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
508515
&info->labels.mask);
509516
err:
510517
skb_push(skb, nh_ofs);
518+
if (err)
519+
kfree_skb(skb);
511520
return err;
512521
}
513522

net/openvswitch/conntrack.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb,
6767
struct sw_flow_key *key,
6868
const struct ovs_conntrack_info *info)
6969
{
70+
kfree_skb(skb);
7071
return -ENOTSUPP;
7172
}
7273

0 commit comments

Comments
 (0)