Skip to content

Commit b90cafa

Browse files
igsilyaNipaLocal
authored andcommitted
net: openvswitch: remove never-working support for setting nsh fields
The validation of the set(nsh(...)) action is completely wrong. It runs through the nsh_key_put_from_nlattr() function that is the same function that validates NSH keys for the flow match and the push_nsh() action. However, the set(nsh(...)) has a very different memory layout. Nested attributes in there are doubled in size in case of the masked set(). That makes proper validation impossible. There is also confusion in the code between the 'masked' flag, that says that the nested attributes are doubled in size containing both the value and the mask, and the 'is_mask' that says that the value we're parsing is the mask. This is causing kernel crash on trying to write into mask part of the match with SW_FLOW_KEY_PUT() during validation, while validate_nsh() doesn't allocate any memory for it: BUG: kernel NULL pointer dereference, address: 0000000000000018 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 1c2383067 P4D 1c2383067 PUD 20b703067 PMD 0 Oops: Oops: 0000 [#1] SMP NOPTI CPU: 8 UID: 0 Kdump: loaded Not tainted 6.17.0-rc4+ torvalds#107 PREEMPT(voluntary) RIP: 0010:nsh_key_put_from_nlattr+0x19d/0x610 [openvswitch] Call Trace: <TASK> validate_nsh+0x60/0x90 [openvswitch] validate_set.constprop.0+0x270/0x3c0 [openvswitch] __ovs_nla_copy_actions+0x477/0x860 [openvswitch] ovs_nla_copy_actions+0x8d/0x100 [openvswitch] ovs_packet_cmd_execute+0x1cc/0x310 [openvswitch] genl_family_rcv_msg_doit+0xdb/0x130 genl_family_rcv_msg+0x14b/0x220 genl_rcv_msg+0x47/0xa0 netlink_rcv_skb+0x53/0x100 genl_rcv+0x24/0x40 netlink_unicast+0x280/0x3b0 netlink_sendmsg+0x1f7/0x430 ____sys_sendmsg+0x36b/0x3a0 ___sys_sendmsg+0x87/0xd0 __sys_sendmsg+0x6d/0xd0 do_syscall_64+0x7b/0x2c0 entry_SYSCALL_64_after_hwframe+0x76/0x7e The third issue with this process is that while trying to convert the non-masked set into masked one, validate_set() copies and doubles the size of the OVS_KEY_ATTR_NSH as if it didn't have any nested attributes. It should be copying each nested attribute and doubling them in size independently. And the process must be properly reversed during the conversion back from masked to a non-masked variant during the flow dump. In the end, the only two outcomes of trying to use this action are either validation failure or a kernel crash. And if somehow someone manages to install a flow with such an action, it will most definitely not do what it is supposed to, since all the keys and the masks are mixed up. Fixing all the issues is a complex task as it requires re-writing most of the validation code. Given that and the fact that this functionality never worked since introduction, let's just remove it altogether. It's better to re-introduce it later with a proper implementation instead of trying to fix it in stable releases. Fixes: b2d0f5d ("openvswitch: enable NSH support") Reported-by: Junvy Yang <[email protected]> Signed-off-by: Ilya Maximets <[email protected]> Acked-by: Eelco Chaudron <[email protected]> Reviewed-by: Aaron Conole <[email protected]> Signed-off-by: NipaLocal <nipa@local>
1 parent 5470a17 commit b90cafa

File tree

3 files changed

+9
-125
lines changed

3 files changed

+9
-125
lines changed

net/openvswitch/actions.c

Lines changed: 1 addition & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -572,69 +572,6 @@ static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *flow_key,
572572
return 0;
573573
}
574574

575-
static int set_nsh(struct sk_buff *skb, struct sw_flow_key *flow_key,
576-
const struct nlattr *a)
577-
{
578-
struct nshhdr *nh;
579-
size_t length;
580-
int err;
581-
u8 flags;
582-
u8 ttl;
583-
int i;
584-
585-
struct ovs_key_nsh key;
586-
struct ovs_key_nsh mask;
587-
588-
err = nsh_key_from_nlattr(a, &key, &mask);
589-
if (err)
590-
return err;
591-
592-
/* Make sure the NSH base header is there */
593-
if (!pskb_may_pull(skb, skb_network_offset(skb) + NSH_BASE_HDR_LEN))
594-
return -ENOMEM;
595-
596-
nh = nsh_hdr(skb);
597-
length = nsh_hdr_len(nh);
598-
599-
/* Make sure the whole NSH header is there */
600-
err = skb_ensure_writable(skb, skb_network_offset(skb) +
601-
length);
602-
if (unlikely(err))
603-
return err;
604-
605-
nh = nsh_hdr(skb);
606-
skb_postpull_rcsum(skb, nh, length);
607-
flags = nsh_get_flags(nh);
608-
flags = OVS_MASKED(flags, key.base.flags, mask.base.flags);
609-
flow_key->nsh.base.flags = flags;
610-
ttl = nsh_get_ttl(nh);
611-
ttl = OVS_MASKED(ttl, key.base.ttl, mask.base.ttl);
612-
flow_key->nsh.base.ttl = ttl;
613-
nsh_set_flags_and_ttl(nh, flags, ttl);
614-
nh->path_hdr = OVS_MASKED(nh->path_hdr, key.base.path_hdr,
615-
mask.base.path_hdr);
616-
flow_key->nsh.base.path_hdr = nh->path_hdr;
617-
switch (nh->mdtype) {
618-
case NSH_M_TYPE1:
619-
for (i = 0; i < NSH_MD1_CONTEXT_SIZE; i++) {
620-
nh->md1.context[i] =
621-
OVS_MASKED(nh->md1.context[i], key.context[i],
622-
mask.context[i]);
623-
}
624-
memcpy(flow_key->nsh.context, nh->md1.context,
625-
sizeof(nh->md1.context));
626-
break;
627-
case NSH_M_TYPE2:
628-
memset(flow_key->nsh.context, 0,
629-
sizeof(flow_key->nsh.context));
630-
break;
631-
default:
632-
return -EINVAL;
633-
}
634-
skb_postpush_rcsum(skb, nh, length);
635-
return 0;
636-
}
637-
638575
/* Must follow skb_ensure_writable() since that can move the skb data. */
639576
static void set_tp_port(struct sk_buff *skb, __be16 *port,
640577
__be16 new_port, __sum16 *check)
@@ -1130,10 +1067,6 @@ static int execute_masked_set_action(struct sk_buff *skb,
11301067
get_mask(a, struct ovs_key_ethernet *));
11311068
break;
11321069

1133-
case OVS_KEY_ATTR_NSH:
1134-
err = set_nsh(skb, flow_key, a);
1135-
break;
1136-
11371070
case OVS_KEY_ATTR_IPV4:
11381071
err = set_ipv4(skb, flow_key, nla_data(a),
11391072
get_mask(a, struct ovs_key_ipv4 *));
@@ -1170,6 +1103,7 @@ static int execute_masked_set_action(struct sk_buff *skb,
11701103
case OVS_KEY_ATTR_CT_LABELS:
11711104
case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4:
11721105
case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6:
1106+
case OVS_KEY_ATTR_NSH:
11731107
err = -EINVAL;
11741108
break;
11751109
}

net/openvswitch/flow_netlink.c

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,11 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
13051305
return 0;
13061306
}
13071307

1308+
/*
1309+
* Constructs NSH header 'nh' from attributes of OVS_ACTION_ATTR_PUSH_NSH,
1310+
* where 'nh' points to a memory block of 'size' bytes. It's assumed that
1311+
* attributes were previously validated with validate_push_nsh().
1312+
*/
13081313
int nsh_hdr_from_nlattr(const struct nlattr *attr,
13091314
struct nshhdr *nh, size_t size)
13101315
{
@@ -1314,8 +1319,6 @@ int nsh_hdr_from_nlattr(const struct nlattr *attr,
13141319
u8 ttl = 0;
13151320
int mdlen = 0;
13161321

1317-
/* validate_nsh has check this, so we needn't do duplicate check here
1318-
*/
13191322
if (size < NSH_BASE_HDR_LEN)
13201323
return -ENOBUFS;
13211324

@@ -1359,46 +1362,6 @@ int nsh_hdr_from_nlattr(const struct nlattr *attr,
13591362
return 0;
13601363
}
13611364

1362-
int nsh_key_from_nlattr(const struct nlattr *attr,
1363-
struct ovs_key_nsh *nsh, struct ovs_key_nsh *nsh_mask)
1364-
{
1365-
struct nlattr *a;
1366-
int rem;
1367-
1368-
/* validate_nsh has check this, so we needn't do duplicate check here
1369-
*/
1370-
nla_for_each_nested(a, attr, rem) {
1371-
int type = nla_type(a);
1372-
1373-
switch (type) {
1374-
case OVS_NSH_KEY_ATTR_BASE: {
1375-
const struct ovs_nsh_key_base *base = nla_data(a);
1376-
const struct ovs_nsh_key_base *base_mask = base + 1;
1377-
1378-
nsh->base = *base;
1379-
nsh_mask->base = *base_mask;
1380-
break;
1381-
}
1382-
case OVS_NSH_KEY_ATTR_MD1: {
1383-
const struct ovs_nsh_key_md1 *md1 = nla_data(a);
1384-
const struct ovs_nsh_key_md1 *md1_mask = md1 + 1;
1385-
1386-
memcpy(nsh->context, md1->context, sizeof(*md1));
1387-
memcpy(nsh_mask->context, md1_mask->context,
1388-
sizeof(*md1_mask));
1389-
break;
1390-
}
1391-
case OVS_NSH_KEY_ATTR_MD2:
1392-
/* Not supported yet */
1393-
return -ENOTSUPP;
1394-
default:
1395-
return -EINVAL;
1396-
}
1397-
}
1398-
1399-
return 0;
1400-
}
1401-
14021365
static int nsh_key_put_from_nlattr(const struct nlattr *attr,
14031366
struct sw_flow_match *match, bool is_mask,
14041367
bool is_push_nsh, bool log)
@@ -2839,17 +2802,13 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
28392802
return err;
28402803
}
28412804

2842-
static bool validate_nsh(const struct nlattr *attr, bool is_mask,
2843-
bool is_push_nsh, bool log)
2805+
static bool validate_push_nsh(const struct nlattr *attr, bool log)
28442806
{
28452807
struct sw_flow_match match;
28462808
struct sw_flow_key key;
2847-
int ret = 0;
28482809

28492810
ovs_match_init(&match, &key, true, NULL);
2850-
ret = nsh_key_put_from_nlattr(attr, &match, is_mask,
2851-
is_push_nsh, log);
2852-
return !ret;
2811+
return !nsh_key_put_from_nlattr(attr, &match, false, true, log);
28532812
}
28542813

28552814
/* Return false if there are any non-masked bits set.
@@ -2997,13 +2956,6 @@ static int validate_set(const struct nlattr *a,
29972956

29982957
break;
29992958

3000-
case OVS_KEY_ATTR_NSH:
3001-
if (eth_type != htons(ETH_P_NSH))
3002-
return -EINVAL;
3003-
if (!validate_nsh(nla_data(a), masked, false, log))
3004-
return -EINVAL;
3005-
break;
3006-
30072959
default:
30082960
return -EINVAL;
30092961
}
@@ -3437,7 +3389,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
34373389
return -EINVAL;
34383390
}
34393391
mac_proto = MAC_PROTO_NONE;
3440-
if (!validate_nsh(nla_data(a), false, true, true))
3392+
if (!validate_push_nsh(nla_data(a), log))
34413393
return -EINVAL;
34423394
break;
34433395

net/openvswitch/flow_netlink.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ int ovs_nla_put_actions(const struct nlattr *attr,
6565
void ovs_nla_free_flow_actions(struct sw_flow_actions *);
6666
void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *);
6767

68-
int nsh_key_from_nlattr(const struct nlattr *attr, struct ovs_key_nsh *nsh,
69-
struct ovs_key_nsh *nsh_mask);
7068
int nsh_hdr_from_nlattr(const struct nlattr *attr, struct nshhdr *nh,
7169
size_t size);
7270

0 commit comments

Comments
 (0)