summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Gross <jesse@nicira.com>2015-09-21 23:21:20 -0400
committerDavid S. Miller <davem@davemloft.net>2015-09-22 20:33:41 -0400
commitae5f2fb1d51fa128a460bcfbe3c56d7ab8bf6a43 (patch)
tree7e05a2752e98068a8d19d95ccad191f896e73b12
parent53adc9e83028d9e35b6408231ebaf62a94a16e4d (diff)
openvswitch: Zero flows on allocation.
When support for megaflows was introduced, OVS needed to start installing flows with a mask applied to them. Since masking is an expensive operation, OVS also had an optimization that would only take the parts of the flow keys that were covered by a non-zero mask. The values stored in the remaining pieces should not matter because they are masked out. While this works fine for the purposes of matching (which must always look at the mask), serialization to netlink can be problematic. Since the flow and the mask are serialized separately, the uninitialized portions of the flow can be encoded with whatever values happen to be present. In terms of functionality, this has little effect since these fields will be masked out by definition. However, it leaks kernel memory to userspace, which is a potential security vulnerability. It is also possible that other code paths could look at the masked key and get uninitialized data, although this does not currently appear to be an issue in practice. This removes the mask optimization for flows that are being installed. This was always intended to be the case as the mask optimizations were really targetting per-packet flow operations. Fixes: 03f0d916 ("openvswitch: Mega flow implementation") Signed-off-by: Jesse Gross <jesse@nicira.com> Acked-by: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/openvswitch/datapath.c4
-rw-r--r--net/openvswitch/flow_table.c23
-rw-r--r--net/openvswitch/flow_table.h2
3 files changed, 15 insertions, 14 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 6fbd2decb19e..b816ff871528 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -952,7 +952,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
952 if (error) 952 if (error)
953 goto err_kfree_flow; 953 goto err_kfree_flow;
954 954
955 ovs_flow_mask_key(&new_flow->key, &key, &mask); 955 ovs_flow_mask_key(&new_flow->key, &key, true, &mask);
956 956
957 /* Extract flow identifier. */ 957 /* Extract flow identifier. */
958 error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], 958 error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID],
@@ -1080,7 +1080,7 @@ static struct sw_flow_actions *get_flow_actions(struct net *net,
1080 struct sw_flow_key masked_key; 1080 struct sw_flow_key masked_key;
1081 int error; 1081 int error;
1082 1082
1083 ovs_flow_mask_key(&masked_key, key, mask); 1083 ovs_flow_mask_key(&masked_key, key, true, mask);
1084 error = ovs_nla_copy_actions(net, a, &masked_key, &acts, log); 1084 error = ovs_nla_copy_actions(net, a, &masked_key, &acts, log);
1085 if (error) { 1085 if (error) {
1086 OVS_NLERR(log, 1086 OVS_NLERR(log,
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
index d22d8e948d0f..f2ea83ba4763 100644
--- a/net/openvswitch/flow_table.c
+++ b/net/openvswitch/flow_table.c
@@ -57,20 +57,21 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range)
57} 57}
58 58
59void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, 59void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
60 const struct sw_flow_mask *mask) 60 bool full, const struct sw_flow_mask *mask)
61{ 61{
62 const long *m = (const long *)((const u8 *)&mask->key + 62 int start = full ? 0 : mask->range.start;
63 mask->range.start); 63 int len = full ? sizeof *dst : range_n_bytes(&mask->range);
64 const long *s = (const long *)((const u8 *)src + 64 const long *m = (const long *)((const u8 *)&mask->key + start);
65 mask->range.start); 65 const long *s = (const long *)((const u8 *)src + start);
66 long *d = (long *)((u8 *)dst + mask->range.start); 66 long *d = (long *)((u8 *)dst + start);
67 int i; 67 int i;
68 68
69 /* The memory outside of the 'mask->range' are not set since 69 /* If 'full' is true then all of 'dst' is fully initialized. Otherwise,
70 * further operations on 'dst' only uses contents within 70 * if 'full' is false the memory outside of the 'mask->range' is left
71 * 'mask->range'. 71 * uninitialized. This can be used as an optimization when further
72 * operations on 'dst' only use contents within 'mask->range'.
72 */ 73 */
73 for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long)) 74 for (i = 0; i < len; i += sizeof(long))
74 *d++ = *s++ & *m++; 75 *d++ = *s++ & *m++;
75} 76}
76 77
@@ -475,7 +476,7 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
475 u32 hash; 476 u32 hash;
476 struct sw_flow_key masked_key; 477 struct sw_flow_key masked_key;
477 478
478 ovs_flow_mask_key(&masked_key, unmasked, mask); 479 ovs_flow_mask_key(&masked_key, unmasked, false, mask);
479 hash = flow_hash(&masked_key, &mask->range); 480 hash = flow_hash(&masked_key, &mask->range);
480 head = find_bucket(ti, hash); 481 head = find_bucket(ti, hash);
481 hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) { 482 hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) {
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h
index 616eda10d955..2dd9900f533d 100644
--- a/net/openvswitch/flow_table.h
+++ b/net/openvswitch/flow_table.h
@@ -86,5 +86,5 @@ struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *,
86bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *); 86bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *);
87 87
88void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, 88void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
89 const struct sw_flow_mask *mask); 89 bool full, const struct sw_flow_mask *mask);
90#endif /* flow_table.h */ 90#endif /* flow_table.h */