Skip to content

Commit 96026b9

Browse files
igsilyaUlrich Hecht
authored andcommitted
net: openvswitch: remove never-working support for setting nsh fields
[ Upstream commit dfe28c4167a9259fc0c372d9f9473e1ac95cff67 ] 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+ #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 <zhuque@tencent.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org> Acked-by: Eelco Chaudron <echaudro@redhat.com> Reviewed-by: Aaron Conole <aconole@redhat.com> Link: https://patch.msgid.link/20251112112246.95064-1-i.maximets@ovn.org Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Ulrich Hecht <uli@kernel.org>
1 parent c4d6c59 commit 96026b9

3 files changed

Lines changed: 9 additions & 125 deletions

File tree

net/openvswitch/actions.c

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

668-
static int set_nsh(struct sk_buff *skb, struct sw_flow_key *flow_key,
669-
const struct nlattr *a)
670-
{
671-
struct nshhdr *nh;
672-
size_t length;
673-
int err;
674-
u8 flags;
675-
u8 ttl;
676-
int i;
677-
678-
struct ovs_key_nsh key;
679-
struct ovs_key_nsh mask;
680-
681-
err = nsh_key_from_nlattr(a, &key, &mask);
682-
if (err)
683-
return err;
684-
685-
/* Make sure the NSH base header is there */
686-
if (!pskb_may_pull(skb, skb_network_offset(skb) + NSH_BASE_HDR_LEN))
687-
return -ENOMEM;
688-
689-
nh = nsh_hdr(skb);
690-
length = nsh_hdr_len(nh);
691-
692-
/* Make sure the whole NSH header is there */
693-
err = skb_ensure_writable(skb, skb_network_offset(skb) +
694-
length);
695-
if (unlikely(err))
696-
return err;
697-
698-
nh = nsh_hdr(skb);
699-
skb_postpull_rcsum(skb, nh, length);
700-
flags = nsh_get_flags(nh);
701-
flags = OVS_MASKED(flags, key.base.flags, mask.base.flags);
702-
flow_key->nsh.base.flags = flags;
703-
ttl = nsh_get_ttl(nh);
704-
ttl = OVS_MASKED(ttl, key.base.ttl, mask.base.ttl);
705-
flow_key->nsh.base.ttl = ttl;
706-
nsh_set_flags_and_ttl(nh, flags, ttl);
707-
nh->path_hdr = OVS_MASKED(nh->path_hdr, key.base.path_hdr,
708-
mask.base.path_hdr);
709-
flow_key->nsh.base.path_hdr = nh->path_hdr;
710-
switch (nh->mdtype) {
711-
case NSH_M_TYPE1:
712-
for (i = 0; i < NSH_MD1_CONTEXT_SIZE; i++) {
713-
nh->md1.context[i] =
714-
OVS_MASKED(nh->md1.context[i], key.context[i],
715-
mask.context[i]);
716-
}
717-
memcpy(flow_key->nsh.context, nh->md1.context,
718-
sizeof(nh->md1.context));
719-
break;
720-
case NSH_M_TYPE2:
721-
memset(flow_key->nsh.context, 0,
722-
sizeof(flow_key->nsh.context));
723-
break;
724-
default:
725-
return -EINVAL;
726-
}
727-
skb_postpush_rcsum(skb, nh, length);
728-
return 0;
729-
}
730-
731668
/* Must follow skb_ensure_writable() since that can move the skb data. */
732669
static void set_tp_port(struct sk_buff *skb, __be16 *port,
733670
__be16 new_port, __sum16 *check)
@@ -1183,10 +1120,6 @@ static int execute_masked_set_action(struct sk_buff *skb,
11831120
get_mask(a, struct ovs_key_ethernet *));
11841121
break;
11851122

1186-
case OVS_KEY_ATTR_NSH:
1187-
err = set_nsh(skb, flow_key, a);
1188-
break;
1189-
11901123
case OVS_KEY_ATTR_IPV4:
11911124
err = set_ipv4(skb, flow_key, nla_data(a),
11921125
get_mask(a, struct ovs_key_ipv4 *));
@@ -1223,6 +1156,7 @@ static int execute_masked_set_action(struct sk_buff *skb,
12231156
case OVS_KEY_ATTR_CT_LABELS:
12241157
case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4:
12251158
case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6:
1159+
case OVS_KEY_ATTR_NSH:
12261160
err = -EINVAL;
12271161
break;
12281162
}

net/openvswitch/flow_netlink.c

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

1268+
/*
1269+
* Constructs NSH header 'nh' from attributes of OVS_ACTION_ATTR_PUSH_NSH,
1270+
* where 'nh' points to a memory block of 'size' bytes. It's assumed that
1271+
* attributes were previously validated with validate_push_nsh().
1272+
*/
12681273
int nsh_hdr_from_nlattr(const struct nlattr *attr,
12691274
struct nshhdr *nh, size_t size)
12701275
{
@@ -1274,8 +1279,6 @@ int nsh_hdr_from_nlattr(const struct nlattr *attr,
12741279
u8 ttl = 0;
12751280
int mdlen = 0;
12761281

1277-
/* validate_nsh has check this, so we needn't do duplicate check here
1278-
*/
12791282
if (size < NSH_BASE_HDR_LEN)
12801283
return -ENOBUFS;
12811284

@@ -1319,46 +1322,6 @@ int nsh_hdr_from_nlattr(const struct nlattr *attr,
13191322
return 0;
13201323
}
13211324

1322-
int nsh_key_from_nlattr(const struct nlattr *attr,
1323-
struct ovs_key_nsh *nsh, struct ovs_key_nsh *nsh_mask)
1324-
{
1325-
struct nlattr *a;
1326-
int rem;
1327-
1328-
/* validate_nsh has check this, so we needn't do duplicate check here
1329-
*/
1330-
nla_for_each_nested(a, attr, rem) {
1331-
int type = nla_type(a);
1332-
1333-
switch (type) {
1334-
case OVS_NSH_KEY_ATTR_BASE: {
1335-
const struct ovs_nsh_key_base *base = nla_data(a);
1336-
const struct ovs_nsh_key_base *base_mask = base + 1;
1337-
1338-
nsh->base = *base;
1339-
nsh_mask->base = *base_mask;
1340-
break;
1341-
}
1342-
case OVS_NSH_KEY_ATTR_MD1: {
1343-
const struct ovs_nsh_key_md1 *md1 = nla_data(a);
1344-
const struct ovs_nsh_key_md1 *md1_mask = md1 + 1;
1345-
1346-
memcpy(nsh->context, md1->context, sizeof(*md1));
1347-
memcpy(nsh_mask->context, md1_mask->context,
1348-
sizeof(*md1_mask));
1349-
break;
1350-
}
1351-
case OVS_NSH_KEY_ATTR_MD2:
1352-
/* Not supported yet */
1353-
return -ENOTSUPP;
1354-
default:
1355-
return -EINVAL;
1356-
}
1357-
}
1358-
1359-
return 0;
1360-
}
1361-
13621325
static int nsh_key_put_from_nlattr(const struct nlattr *attr,
13631326
struct sw_flow_match *match, bool is_mask,
13641327
bool is_push_nsh, bool log)
@@ -2670,17 +2633,13 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
26702633
return err;
26712634
}
26722635

2673-
static bool validate_nsh(const struct nlattr *attr, bool is_mask,
2674-
bool is_push_nsh, bool log)
2636+
static bool validate_push_nsh(const struct nlattr *attr, bool log)
26752637
{
26762638
struct sw_flow_match match;
26772639
struct sw_flow_key key;
2678-
int ret = 0;
26792640

26802641
ovs_match_init(&match, &key, true, NULL);
2681-
ret = nsh_key_put_from_nlattr(attr, &match, is_mask,
2682-
is_push_nsh, log);
2683-
return !ret;
2642+
return !nsh_key_put_from_nlattr(attr, &match, false, true, log);
26842643
}
26852644

26862645
/* Return false if there are any non-masked bits set.
@@ -2826,13 +2785,6 @@ static int validate_set(const struct nlattr *a,
28262785

28272786
break;
28282787

2829-
case OVS_KEY_ATTR_NSH:
2830-
if (eth_type != htons(ETH_P_NSH))
2831-
return -EINVAL;
2832-
if (!validate_nsh(nla_data(a), masked, false, log))
2833-
return -EINVAL;
2834-
break;
2835-
28362788
default:
28372789
return -EINVAL;
28382790
}
@@ -3102,7 +3054,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
31023054
return -EINVAL;
31033055
}
31043056
mac_proto = MAC_PROTO_NONE;
3105-
if (!validate_nsh(nla_data(a), false, true, true))
3057+
if (!validate_push_nsh(nla_data(a), log))
31063058
return -EINVAL;
31073059
break;
31083060

net/openvswitch/flow_netlink.h

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

82-
int nsh_key_from_nlattr(const struct nlattr *attr, struct ovs_key_nsh *nsh,
83-
struct ovs_key_nsh *nsh_mask);
8482
int nsh_hdr_from_nlattr(const struct nlattr *attr, struct nshhdr *nh,
8583
size_t size);
8684

0 commit comments

Comments
 (0)