Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jesse Gross and committed by
David S. Miller
ae5f2fb1 53adc9e8

+15 -14
+2 -2
net/openvswitch/datapath.c
··· 952 952 if (error) 953 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 957 /* Extract flow identifier. */ 958 958 error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], ··· 1080 1080 struct sw_flow_key masked_key; 1081 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 1084 error = ovs_nla_copy_actions(net, a, &masked_key, &acts, log); 1085 1085 if (error) { 1086 1086 OVS_NLERR(log,
+12 -11
net/openvswitch/flow_table.c
··· 57 57 } 58 58 59 59 void 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 + 63 - mask->range.start); 64 - const long *s = (const long *)((const u8 *)src + 65 - mask->range.start); 66 - long *d = (long *)((u8 *)dst + mask->range.start); 62 + int start = full ? 0 : mask->range.start; 63 + int len = full ? sizeof *dst : range_n_bytes(&mask->range); 64 + const long *m = (const long *)((const u8 *)&mask->key + start); 65 + const long *s = (const long *)((const u8 *)src + start); 66 + long *d = (long *)((u8 *)dst + start); 67 67 int i; 68 68 69 - /* The memory outside of the 'mask->range' are not set since 70 - * further operations on 'dst' only uses contents within 71 - * 'mask->range'. 69 + /* If 'full' is true then all of 'dst' is fully initialized. Otherwise, 70 + * if 'full' is false the memory outside of the 'mask->range' is left 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 75 *d++ = *s++ & *m++; 75 76 } 76 77 ··· 476 475 u32 hash; 477 476 struct sw_flow_key masked_key; 478 477 479 - ovs_flow_mask_key(&masked_key, unmasked, mask); 478 + ovs_flow_mask_key(&masked_key, unmasked, false, mask); 480 479 hash = flow_hash(&masked_key, &mask->range); 481 480 head = find_bucket(ti, hash); 482 481 hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) {
+1 -1
net/openvswitch/flow_table.h
··· 86 86 bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *); 87 87 88 88 void 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 90 #endif /* flow_table.h */