Blob Blame History Raw
From d614648206c87c5d068f9dec596c9dfb152a5618 Mon Sep 17 00:00:00 2001
From: Alaa Hleihel <ahleihel@redhat.com>
Date: Sun, 10 May 2020 15:04:42 -0400
Subject: [PATCH 117/312] [netdrv] net/mlx5e: Avoid duplicating rule
 destinations

Message-id: <20200510150452.10307-78-ahleihel@redhat.com>
Patchwork-id: 306701
Patchwork-instance: patchwork
O-Subject: [RHEL8.3 BZ 1789380 v2 77/87] net/mlx5e: Avoid duplicating rule destinations
Bugzilla: 1789380
RH-Acked-by: Kamal Heib <kheib@redhat.com>
RH-Acked-by: Jarod Wilson <jarod@redhat.com>
RH-Acked-by: Tony Camuso <tcamuso@redhat.com>
RH-Acked-by: Jonathan Toppins <jtoppins@redhat.com>

Bugzilla: http://bugzilla.redhat.com/1789380
Upstream: v5.5-rc6
Conflicts:
 - drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
   Context diff due to missing commit:
   84179981317f ("net/mlx5: TC: Offload flow table rules")
   ---> Local bool ft_flow variable does not exist yet in function
        parse_tc_fdb_actions

commit 554fe75c1b3f679b1eebf193a4e56492837d3f5a
Author: Dmytro Linkin <dmitrolin@mellanox.com>
Date:   Thu Oct 31 18:15:51 2019 +0200

    net/mlx5e: Avoid duplicating rule destinations

    Following scenario easily break driver logic and crash the kernel:
    1. Add rule with mirred actions to same device.
    2. Delete this rule.
    In described scenario rule is not added to database and on deletion
    driver access invalid entry.
    Example:

     $ tc filter add dev ens1f0_0 ingress protocol ip prio 1 \
           flower skip_sw \
           action mirred egress mirror dev ens1f0_1 pipe \
           action mirred egress redirect dev ens1f0_1
     $ tc filter del dev ens1f0_0 ingress protocol ip prio 1

    Dmesg output:

    [  376.634396] mlx5_core 0000:82:00.0: mlx5_cmd_check:756:(pid 3439): DESTROY_FLOW_GROUP(0x934) op_mod(0x0) failed, status bad resource state(0x9), syndrome (0x563e2f)
    [  376.654983] mlx5_core 0000:82:00.0: del_hw_flow_group:567:(pid 3439): flow steering can't destroy fg 89 of ft 3145728
    [  376.673433] kasan: CONFIG_KASAN_INLINE enabled
    [  376.683769] kasan: GPF could be caused by NULL-ptr deref or user memory access
    [  376.695229] general protection fault: 0000 [#1] PREEMPT SMP KASAN PTI
    [  376.705069] CPU: 7 PID: 3439 Comm: tc Not tainted 5.4.0-rc5+ #76
    [  376.714959] Hardware name: Supermicro SYS-2028TP-DECTR/X10DRT-PT, BIOS 2.0a 08/12/2016
    [  376.726371] RIP: 0010:mlx5_del_flow_rules+0x105/0x960 [mlx5_core]
    [  376.735817] Code: 01 00 00 00 48 83 eb 08 e8 28 d9 ff ff 4c 39 e3 75 d8 4c 8d bd c0 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 89 fa 48 c1 ea 03 <0f> b6 04 02 84 c0 74 08 3c 03 0f 8e 84 04 00 00 48 8d 7d 28 8b 9 d
    [  376.761261] RSP: 0018:ffff888847c56db8 EFLAGS: 00010202
    [  376.770054] RAX: dffffc0000000000 RBX: ffff8888582a6da0 RCX: ffff888847c56d60
    [  376.780743] RDX: 0000000000000058 RSI: 0000000000000008 RDI: 0000000000000282
    [  376.791328] RBP: 0000000000000000 R08: fffffbfff0c60ea6 R09: fffffbfff0c60ea6
    [  376.802050] R10: fffffbfff0c60ea5 R11: ffffffff8630752f R12: ffff8888582a6da0
    [  376.812798] R13: dffffc0000000000 R14: ffff8888582a6da0 R15: 00000000000002c0
    [  376.823445] FS:  00007f675f9a8840(0000) GS:ffff88886d200000(0000) knlGS:0000000000000000
    [  376.834971] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
    [  376.844179] CR2: 00000000007d9640 CR3: 00000007d3f26003 CR4: 00000000001606e0
    [  376.854843] Call Trace:
    [  376.868542]  __mlx5_eswitch_del_rule+0x49/0x300 [mlx5_core]
    [  376.877735]  mlx5e_tc_del_fdb_flow+0x6ec/0x9e0 [mlx5_core]
    [  376.921549]  mlx5e_flow_put+0x2b/0x50 [mlx5_core]
    [  376.929813]  mlx5e_delete_flower+0x5b6/0xbd0 [mlx5_core]
    [  376.973030]  tc_setup_cb_reoffload+0x29/0xc0
    [  376.980619]  fl_reoffload+0x50a/0x770 [cls_flower]
    [  377.015087]  tcf_block_playback_offloads+0xbd/0x250
    [  377.033400]  tcf_block_setup+0x1b2/0xc60
    [  377.057247]  tcf_block_offload_cmd+0x195/0x240
    [  377.098826]  tcf_block_offload_unbind+0xe7/0x180
    [  377.107056]  __tcf_block_put+0xe5/0x400
    [  377.114528]  ingress_destroy+0x3d/0x60 [sch_ingress]
    [  377.122894]  qdisc_destroy+0xf1/0x5a0
    [  377.129993]  qdisc_graft+0xa3d/0xe50
    [  377.151227]  tc_get_qdisc+0x48e/0xa20
    [  377.165167]  rtnetlink_rcv_msg+0x35d/0x8d0
    [  377.199528]  netlink_rcv_skb+0x11e/0x340
    [  377.219638]  netlink_unicast+0x408/0x5b0
    [  377.239913]  netlink_sendmsg+0x71b/0xb30
    [  377.267505]  sock_sendmsg+0xb1/0xf0
    [  377.273801]  ___sys_sendmsg+0x635/0x900
    [  377.312784]  __sys_sendmsg+0xd3/0x170
    [  377.338693]  do_syscall_64+0x95/0x460
    [  377.344833]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
    [  377.352321] RIP: 0033:0x7f675e58e090

    To avoid this, for every mirred action check if output device was
    already processed. If so - drop rule with EOPNOTSUPP error.

    Signed-off-by: Dmytro Linkin <dmitrolin@mellanox.com>
    Reviewed-by: Roi Dayan <roid@mellanox.com>
    Reviewed-by: Vlad Buslov <vladbu@mellanox.com>
    Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>

Signed-off-by: Alaa Hleihel <ahleihel@redhat.com>
Signed-off-by: Frantisek Hrbata <fhrbata@redhat.com>
---
 drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 58 ++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 8afafb7eeb55..0af1d5b1e438 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -2988,6 +2988,25 @@ static struct ip_tunnel_info *dup_tun_info(const struct ip_tunnel_info *tun_info
 	return kmemdup(tun_info, tun_size, GFP_KERNEL);
 }
 
+static bool is_duplicated_encap_entry(struct mlx5e_priv *priv,
+				      struct mlx5e_tc_flow *flow,
+				      int out_index,
+				      struct mlx5e_encap_entry *e,
+				      struct netlink_ext_ack *extack)
+{
+	int i;
+
+	for (i = 0; i < out_index; i++) {
+		if (flow->encaps[i].e != e)
+			continue;
+		NL_SET_ERR_MSG_MOD(extack, "can't duplicate encap action");
+		netdev_err(priv->netdev, "can't duplicate encap action\n");
+		return true;
+	}
+
+	return false;
+}
+
 static int mlx5e_attach_encap(struct mlx5e_priv *priv,
 			      struct mlx5e_tc_flow *flow,
 			      struct net_device *mirred_dev,
@@ -3023,6 +3042,12 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
 
 	/* must verify if encap is valid or not */
 	if (e) {
+		/* Check that entry was not already attached to this flow */
+		if (is_duplicated_encap_entry(priv, flow, out_index, e, extack)) {
+			err = -EOPNOTSUPP;
+			goto out_err;
+		}
+
 		mutex_unlock(&esw->offloads.encap_tbl_lock);
 		wait_for_completion(&e->res_ready);
 
@@ -3209,6 +3234,26 @@ bool mlx5e_is_valid_eswitch_fwd_dev(struct mlx5e_priv *priv,
 	       same_hw_devs(priv, netdev_priv(out_dev));
 }
 
+static bool is_duplicated_output_device(struct net_device *dev,
+					struct net_device *out_dev,
+					int *ifindexes, int if_count,
+					struct netlink_ext_ack *extack)
+{
+	int i;
+
+	for (i = 0; i < if_count; i++) {
+		if (ifindexes[i] == out_dev->ifindex) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "can't duplicate output to same device");
+			netdev_err(dev, "can't duplicate output to same device: %s\n",
+				   out_dev->name);
+			return true;
+		}
+	}
+
+	return false;
+}
+
 static int parse_tc_fdb_actions(struct mlx5e_priv *priv,
 				struct flow_action *flow_action,
 				struct mlx5e_tc_flow *flow,
@@ -3220,10 +3265,11 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv,
 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
 	const struct ip_tunnel_info *info = NULL;
+	int ifindexes[MLX5_MAX_FLOW_FWD_VPORTS];
 	const struct flow_action_entry *act;
+	int err, i, if_count = 0;
 	bool encap = false;
 	u32 action = 0;
-	int err, i;
 
 	if (!flow_action_has_entries(flow_action))
 		return -EINVAL;
@@ -3292,6 +3338,16 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv,
 				struct net_device *uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
 				struct net_device *uplink_upper;
 
+				if (is_duplicated_output_device(priv->netdev,
+								out_dev,
+								ifindexes,
+								if_count,
+								extack))
+					return -EOPNOTSUPP;
+
+				ifindexes[if_count] = out_dev->ifindex;
+				if_count++;
+
 				rcu_read_lock();
 				uplink_upper =
 					netdev_master_upper_dev_get_rcu(uplink_dev);
-- 
2.13.6