All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters
@ 2019-02-15 23:06 Davide Caratti
  2019-02-15 23:06 ` [PATCH RFC 1/5] net/sched: fix refcount leak when 'goto_chain' is used Davide Caratti
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Davide Caratti @ 2019-02-15 23:06 UTC (permalink / raw)
  To: Jamal Hadi Salim, Cong Wang, Jiri Pirko
  Cc: David S. Miller, Vlad Buslov, Paolo Abeni, netdev

currently, the kernel checks for bad values of the control action in
tcf_action_init_1(), after a successful call to the action's init()
function. This causes three bad behaviors:

1. the "half configuration"
   if the action is overwritten, the new configuration data are
   applied successfully

    # tc action add action gact drop index 100
    # tc action replace action gact goto chain 66 index 100 \
    > cookie aabbccdd
    Error: Failed to init TC action chain
    We have an error talking to the kernel
    # tc action show action gact
    total acts 1
     action order 0: gact action goto chain 66
     random type none pass val 0
     index 100 ref 1 bind 0
    cookie aabbccdd

2. a "refcount leak that makes kmemleak complain"
   when a valid 'goto chain' action is overwritten with another 'goto chain'
   action, the kernel leaks two refcounts.
   
   # tc chain add dev dd0 chain 42 ingress protocol ip flower \
   > ip_proto tcp action drop
   # tc chain add dev dd0 chain 43 ingress protocol ip flower \
   > ip_proto udp action drop
   # tc filter add dev dd0 ingress matchall \
   > action goto chain 42 index 66
   # tc action replace action gact goto chain 43 index 66
   Error: Failed to init TC action chain.     
   We have an error talking to the kernel

   # echo scan >/sys/kernel/debug/kmemleak
   <...>
   unreferenced object 0xffff93c0ee09f000 (size 1024):
   comm "tc", pid 2565, jiffies 4295339808 (age 65.426s)
   hex dump (first 32 bytes):
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00 00 00 00 08 00 06 00 00 00 00 00 00 00 00 00  ................
   backtrace:
     [<000000009b63f92d>] tc_ctl_chain+0x3d2/0x4c0
     [<00000000683a8d72>] rtnetlink_rcv_msg+0x263/0x2d0
     [<00000000ddd88f8e>] netlink_rcv_skb+0x4a/0x110
     [<000000006126a348>] netlink_unicast+0x1a0/0x250
     [<00000000b3340877>] netlink_sendmsg+0x2c1/0x3c0
     [<00000000a25a2171>] sock_sendmsg+0x36/0x40
     [<00000000f19ee1ec>] ___sys_sendmsg+0x280/0x2f0
     [<00000000d0422042>] __sys_sendmsg+0x5e/0xa0
     [<000000007a6c61f9>] do_syscall_64+0x5b/0x180
     [<00000000ccd07542>] entry_SYSCALL_64_after_hwframe+0x44/0xa9
     [<0000000013eaa334>] 0xffffffffffffffff

3. a "kernel crash in the traffic plane"
   when an action is overwritten with an invalid 'goto chain' action,
   packets hitting the new rule will trigger a NULL pointer dereference:

   # tc qdisc add dev crash0 clsact
   # tc filter add dev crash0 egress matchall action csum icmp index 6
   # tc action replace action csum icmp goto chain 42 index 6 cookie c1a0c1a0
   # ping 1.2.3.4 -Icrash0

   BUG: unable to handle kernel NULL pointer dereference at 0000000000000000
   #PF error: [normal kernel read fault]
   PGD 0 P4D 0
   Oops: 0000 [#1] SMP PTI
   CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.0.0-rc4.splash #516
   Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011
   RIP: 0010:tcf_action_exec+0xb5/0x100
   Code: 00 00 00 20 74 1d 83 f8 03 75 09 49 83 c4 08 4d 39 ec 75 bc 48 83 c4 10 5b 5d 41 5c 41 5d 41 5e 41 5f c3 49 8b 97 a8 00 00 00 <48> 8b 12 48 89 55 00 48 83 c4 10 5b 5d 41 5c 41 5d 41 5e 41 5f c3
   RSP: 0018:ffff99933da03be0 EFLAGS: 00010246
   RAX: 000000002000002a RBX: ffff99933c463300 RCX: 0000000000003a00
   RDX: 0000000000000000 RSI: ffff9992ebda2828 RDI: ffff9992ebda2818
   RBP: ffff99933da03c80 R08: 0000000032250000 R09: 0000000000000003
   R10: 000000000000003f R11: 0000000000000028 R12: ffff9992ed22c100
   R13: ffff9992ed22c108 R14: 0000000000000001 R15: ffff9993339919c0
   FS:  0000000000000000(0000) GS:ffff99933da00000(0000) knlGS:0000000000000000
   CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
   CR2: 0000000000000000 CR3: 0000000005a0e004 CR4: 00000000001606f0
   Call Trace:
    <IRQ>
    tcf_classify+0x58/0x110
    __dev_queue_xmit+0x407/0x890
    ? ip6_finish_output2+0x366/0x590
    ip6_finish_output2+0x366/0x590
    ? ip6_output+0x68/0x110
    ip6_output+0x68/0x110
    ? nf_hook.constprop.35+0x79/0xc0
    mld_sendpack+0x16f/0x220
    mld_ifc_timer_expire+0x195/0x2c0
    ? igmp6_timer_handler+0x70/0x70
    call_timer_fn+0x2b/0x130
    run_timer_softirq+0x3e8/0x440
    ? tick_sched_timer+0x37/0x70
    __do_softirq+0xe3/0x2f5
    irq_exit+0xf0/0x100
    smp_apic_timer_interrupt+0x6c/0x130
    apic_timer_interrupt+0xf/0x20
    </IRQ>
   RIP: 0010:native_safe_halt+0x2/0x10
   Code: 74 ff ff ff 7f f3 c3 65 48 8b 04 25 00 5c 01 00 f0 80 48 02 20 48 8b 00 a8 08 74 8b eb c1 90 90 90 90 90 90 90 90 90 90 fb f4 <c3> 0f 1f 00 66 2e 0f 1f 84 00 00 00 00 00 f4 c3 90 90 90 90 90 90
   RSP: 0018:ffffffff8be03e98 EFLAGS: 00000246 ORIG_RAX: ffffffffffffff13
   RAX: ffffffff8b417730 RBX: 0000000000000000 RCX: 7ffffb503267b27c
   RDX: 0000000000000001 RSI: 0000000000000000 RDI: ffff99933da1d240
   RBP: 0000000000000000 R08: 000c515677542b91 R09: 0000000000000000
   R10: ffffb312c0cffd98 R11: 0000000000000001 R12: 0000000000000000

all these three problems can be fixed if we validate the control action
in the init() function, in the same way as we are already doing for all
the other parameters.

- patch 1 is a temporary fix for problem 2), but it's reverted at the
  end of the series
- we need to refcount and unrefcount chains within init(): patch 2
  extends the init() prototype to correctly handle 'goto chain' control
  actions.
- patch 3 to [n] fix all the actions, providing a proper error path in
  case of bad control action, thus providing a fix for problems 1), 2)
  and 3)
- patch [n+1] removes two if statements in tcf_action_init_1() causing
  the above problems:
    if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN)) {
     ....
    }
    
    and
    if (!tcf_action_valid(a->tcfa_action)) {
     ....
    }
  as they are not not needed anymore, and for the same reason reverts
  patch 1.

This RFC series fixes only csum, bpf and gact - so it stops with n=5.
In case there are not objections, I will prepare a series fixing all
the actions and send it targeting the 'net' tree (or also 'net-next',
this behavior is reproducible since the first introduction of 'goto
action').

Any feedback is appreciated, thank you in advance!

Davide Caratti (5):
  net/sched: fix refcount leak when 'goto_chain' is used
  net/sched: prepare TC actions to properly validate the control action
  net/sched: act_bpf: validate the control action inside init()
  net/sched: act_csum: validate the control action inside init()
  net/sched: act_gact: validate the control action inside init()

 include/net/act_api.h      |  9 +++++-
 net/sched/act_api.c        | 63 ++++++++++++++++++++++++++++++++++++--
 net/sched/act_bpf.c        | 12 ++++++--
 net/sched/act_connmark.c   |  1 +
 net/sched/act_csum.c       | 22 ++++++++++---
 net/sched/act_gact.c       | 17 ++++++++--
 net/sched/act_ife.c        |  2 +-
 net/sched/act_ipt.c        | 11 ++++---
 net/sched/act_mirred.c     |  1 +
 net/sched/act_nat.c        |  3 +-
 net/sched/act_pedit.c      |  2 +-
 net/sched/act_police.c     |  1 +
 net/sched/act_sample.c     |  2 +-
 net/sched/act_simple.c     |  2 +-
 net/sched/act_skbedit.c    |  1 +
 net/sched/act_skbmod.c     |  1 +
 net/sched/act_tunnel_key.c |  1 +
 net/sched/act_vlan.c       |  2 +-
 18 files changed, 131 insertions(+), 22 deletions(-)

-- 
2.20.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH RFC 1/5] net/sched: fix refcount leak when 'goto_chain' is used
  2019-02-15 23:06 [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Davide Caratti
@ 2019-02-15 23:06 ` Davide Caratti
  2019-02-15 23:06 ` [PATCH RFC 2/5] net/sched: prepare TC actions to properly validate the control action Davide Caratti
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Davide Caratti @ 2019-02-15 23:06 UTC (permalink / raw)
  To: Jamal Hadi Salim, Cong Wang, Jiri Pirko
  Cc: David S. Miller, Vlad Buslov, Paolo Abeni, netdev

when replacing valid 'goto chain' actions with another valid 'goto chain'
action, the kernel leaks chain->action_refcnt and chain->refcnt. Since we
unconditionally take the refcount again, if the control action is a 'goto
chain', we can just drop them after ->init() has ended successfully.

Fixes: db50514f9a9c ("net: sched: add termination action to allow goto chain")
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
---
 net/sched/act_api.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index d4b8355737d8..91d79fac8cb2 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -907,6 +907,11 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 	if (err != ACT_P_CREATED)
 		module_put(a_o->owner);
 
+	if (a->goto_chain) {
+		tcf_action_goto_chain_fini(a);
+		a->goto_chain = NULL;
+	}
+
 	if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN)) {
 		err = tcf_action_goto_chain_init(a, tp);
 		if (err) {
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH RFC 2/5] net/sched: prepare TC actions to properly validate the control action
  2019-02-15 23:06 [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Davide Caratti
  2019-02-15 23:06 ` [PATCH RFC 1/5] net/sched: fix refcount leak when 'goto_chain' is used Davide Caratti
@ 2019-02-15 23:06 ` Davide Caratti
  2019-02-18 16:52   ` Vlad Buslov
  2019-02-15 23:06 ` [PATCH RFC 3/5] net/sched: act_bpf: validate the control action inside init() Davide Caratti
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Davide Caratti @ 2019-02-15 23:06 UTC (permalink / raw)
  To: Jamal Hadi Salim, Cong Wang, Jiri Pirko
  Cc: David S. Miller, Vlad Buslov, Paolo Abeni, netdev

- add tcf_action_check_ctrlact(), and pass a pointer to struct tcf_proto
  in each actions's init() function, to allow validation of 'goto chain'
  control action.
- add tcf_action_set_ctrlact(), to set the control action, release the
  previous 'goto_chain' handle and replace it with the new one.

Signed-off-by: Davide Caratti <dcaratti@redhat.com>
---
 include/net/act_api.h      |  9 +++++-
 net/sched/act_api.c        | 58 ++++++++++++++++++++++++++++++++++++--
 net/sched/act_bpf.c        |  2 +-
 net/sched/act_connmark.c   |  1 +
 net/sched/act_csum.c       |  2 +-
 net/sched/act_gact.c       |  2 +-
 net/sched/act_ife.c        |  2 +-
 net/sched/act_ipt.c        | 11 ++++----
 net/sched/act_mirred.c     |  1 +
 net/sched/act_nat.c        |  3 +-
 net/sched/act_pedit.c      |  2 +-
 net/sched/act_police.c     |  1 +
 net/sched/act_sample.c     |  2 +-
 net/sched/act_simple.c     |  2 +-
 net/sched/act_skbedit.c    |  1 +
 net/sched/act_skbmod.c     |  1 +
 net/sched/act_tunnel_key.c |  1 +
 net/sched/act_vlan.c       |  2 +-
 18 files changed, 86 insertions(+), 17 deletions(-)

diff --git a/include/net/act_api.h b/include/net/act_api.h
index dbc795ec659e..55a8d44ac0c7 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -90,7 +90,7 @@ struct tc_action_ops {
 	int     (*lookup)(struct net *net, struct tc_action **a, u32 index);
 	int     (*init)(struct net *net, struct nlattr *nla,
 			struct nlattr *est, struct tc_action **act, int ovr,
-			int bind, bool rtnl_held,
+			int bind, bool rtnl_held, struct tcf_proto *tp,
 			struct netlink_ext_ack *extack);
 	int     (*walk)(struct net *, struct sk_buff *,
 			struct netlink_callback *, int,
@@ -181,6 +181,13 @@ int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
 int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int);
 int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int);
 
+int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
+			     struct tcf_chain **handle,
+			     struct netlink_ext_ack *extack);
+
+void tcf_action_set_ctrlact(struct tc_action *p, int action,
+			    struct tcf_chain *goto_chain);
+
 #endif /* CONFIG_NET_CLS_ACT */
 
 static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes,
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 91d79fac8cb2..088b0d846bde 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -71,6 +71,60 @@ static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
 		call_rcu(&old->rcu, tcf_free_cookie_rcu);
 }
 
+int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
+			     struct tcf_chain **handle,
+			     struct netlink_ext_ack *extack)
+{
+	int opcode = TC_ACT_EXT_OPCODE(action), ret = -EINVAL;
+	u32 chain_index;
+
+	if (!opcode)
+		ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0;
+	else if (opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC)
+		ret = 0;
+	if (ret) {
+		NL_SET_ERR_MSG(extack, "invalid control action");
+		goto end;
+	}
+
+	if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) {
+		chain_index = action & TC_ACT_EXT_VAL_MASK;
+		if (!tp) {
+			ret = -EINVAL;
+			NL_SET_ERR_MSG(extack,
+				       "can't use goto_chain with NULL proto");
+			goto end;
+		}
+		if (!handle) {
+			ret = -EINVAL;
+			NL_SET_ERR_MSG(extack,
+				       "can't put goto_chain on NULL handle");
+			goto end;
+		}
+		*handle = tcf_chain_get_by_act(tp->chain->block, chain_index);
+		if (!*handle) {
+			ret = -ENOMEM;
+			NL_SET_ERR_MSG(extack,
+				       "can't allocate goto_chain handle");
+		}
+	}
+end:
+	return ret;
+}
+EXPORT_SYMBOL(tcf_action_check_ctrlact);
+
+void tcf_action_set_ctrlact(struct tc_action *p, int action,
+			    struct tcf_chain *goto_chain)
+{
+	struct tcf_chain *old;
+
+	old = xchg(&p->goto_chain, goto_chain);
+	if (old)
+		tcf_chain_put_by_act(old);
+	p->tcfa_action = action;
+}
+EXPORT_SYMBOL(tcf_action_set_ctrlact);
+
 /* XXX: For standalone actions, we don't need a RCU grace period either, because
  * actions are always connected to filters and filters are already destroyed in
  * RCU callbacks, so after a RCU grace period actions are already disconnected
@@ -890,10 +944,10 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 	/* backward compatibility for policer */
 	if (name == NULL)
 		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind,
-				rtnl_held, extack);
+				rtnl_held, tp, extack);
 	else
 		err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held,
-				extack);
+				tp, extack);
 	if (err < 0)
 		goto err_mod;
 
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
index c7633843e223..88a729bdab25 100644
--- a/net/sched/act_bpf.c
+++ b/net/sched/act_bpf.c
@@ -278,7 +278,7 @@ static void tcf_bpf_prog_fill_cfg(const struct tcf_bpf *prog,
 static int tcf_bpf_init(struct net *net, struct nlattr *nla,
 			struct nlattr *est, struct tc_action **act,
 			int replace, int bind, bool rtnl_held,
-			struct netlink_ext_ack *extack)
+			struct tcf_proto *tp, struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, bpf_net_id);
 	struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c
index 8475913f2070..30c4c109c80c 100644
--- a/net/sched/act_connmark.c
+++ b/net/sched/act_connmark.c
@@ -97,6 +97,7 @@ static const struct nla_policy connmark_policy[TCA_CONNMARK_MAX + 1] = {
 static int tcf_connmark_init(struct net *net, struct nlattr *nla,
 			     struct nlattr *est, struct tc_action **a,
 			     int ovr, int bind, bool rtnl_held,
+			     struct tcf_proto *tp,
 			     struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, connmark_net_id);
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index 3dc25b7806d7..1ae120c9ab02 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -46,7 +46,7 @@ static struct tc_action_ops act_csum_ops;
 
 static int tcf_csum_init(struct net *net, struct nlattr *nla,
 			 struct nlattr *est, struct tc_action **a, int ovr,
-			 int bind, bool rtnl_held,
+			 int bind, bool rtnl_held, struct tcf_proto *tp,
 			 struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, csum_net_id);
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index b61c20ebb314..727bbca9534b 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -57,7 +57,7 @@ static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = {
 static int tcf_gact_init(struct net *net, struct nlattr *nla,
 			 struct nlattr *est, struct tc_action **a,
 			 int ovr, int bind, bool rtnl_held,
-			 struct netlink_ext_ack *extack)
+			 struct tcf_proto *tp, struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, gact_net_id);
 	struct nlattr *tb[TCA_GACT_MAX + 1];
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
index 30b63fa23ee2..9b2eb941e093 100644
--- a/net/sched/act_ife.c
+++ b/net/sched/act_ife.c
@@ -469,7 +469,7 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
 static int tcf_ife_init(struct net *net, struct nlattr *nla,
 			struct nlattr *est, struct tc_action **a,
 			int ovr, int bind, bool rtnl_held,
-			struct netlink_ext_ack *extack)
+			struct tcf_proto *tp, struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, ife_net_id);
 	struct nlattr *tb[TCA_IFE_MAX + 1];
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 8af6c11d2482..4b3c844ca988 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -97,7 +97,8 @@ static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
 
 static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
 			  struct nlattr *est, struct tc_action **a,
-			  const struct tc_action_ops *ops, int ovr, int bind)
+			  const struct tc_action_ops *ops, int ovr, int bind,
+			  struct tcf_proto *tp)
 {
 	struct tc_action_net *tn = net_generic(net, id);
 	struct nlattr *tb[TCA_IPT_MAX + 1];
@@ -206,20 +207,20 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
 
 static int tcf_ipt_init(struct net *net, struct nlattr *nla,
 			struct nlattr *est, struct tc_action **a, int ovr,
-			int bind, bool rtnl_held,
+			int bind, bool rtnl_held, struct tcf_proto *tp,
 			struct netlink_ext_ack *extack)
 {
 	return __tcf_ipt_init(net, ipt_net_id, nla, est, a, &act_ipt_ops, ovr,
-			      bind);
+			      bind, tp);
 }
 
 static int tcf_xt_init(struct net *net, struct nlattr *nla,
 		       struct nlattr *est, struct tc_action **a, int ovr,
-		       int bind, bool unlocked,
+		       int bind, bool unlocked, struct tcf_proto *tp,
 		       struct netlink_ext_ack *extack)
 {
 	return __tcf_ipt_init(net, xt_net_id, nla, est, a, &act_xt_ops, ovr,
-			      bind);
+			      bind, tp);
 }
 
 static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a,
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index c8cf4d10c435..69dda57f1097 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -94,6 +94,7 @@ static struct tc_action_ops act_mirred_ops;
 static int tcf_mirred_init(struct net *net, struct nlattr *nla,
 			   struct nlattr *est, struct tc_action **a,
 			   int ovr, int bind, bool rtnl_held,
+			   struct tcf_proto *tp,
 			   struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, mirred_net_id);
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
index c5c1e23add77..526c4c99bcce 100644
--- a/net/sched/act_nat.c
+++ b/net/sched/act_nat.c
@@ -38,7 +38,8 @@ static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
 
 static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
 			struct tc_action **a, int ovr, int bind,
-			bool rtnl_held, struct netlink_ext_ack *extack)
+			bool rtnl_held,	struct tcf_proto *tp,
+			struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, nat_net_id);
 	struct nlattr *tb[TCA_NAT_MAX + 1];
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 2b372a06b432..1c7a0db7b466 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -138,7 +138,7 @@ static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
 static int tcf_pedit_init(struct net *net, struct nlattr *nla,
 			  struct nlattr *est, struct tc_action **a,
 			  int ovr, int bind, bool rtnl_held,
-			  struct netlink_ext_ack *extack)
+			  struct tcf_proto *tp, struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, pedit_net_id);
 	struct nlattr *tb[TCA_PEDIT_MAX + 1];
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index ec8ec55e0fe8..a444dd78a244 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -83,6 +83,7 @@ static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
 static int tcf_police_init(struct net *net, struct nlattr *nla,
 			       struct nlattr *est, struct tc_action **a,
 			       int ovr, int bind, bool rtnl_held,
+			       struct tcf_proto *tp,
 			       struct netlink_ext_ack *extack)
 {
 	int ret = 0, tcfp_result = TC_ACT_OK, err, size;
diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c
index 1a0c682fd734..b2154edcb535 100644
--- a/net/sched/act_sample.c
+++ b/net/sched/act_sample.c
@@ -37,7 +37,7 @@ static const struct nla_policy sample_policy[TCA_SAMPLE_MAX + 1] = {
 
 static int tcf_sample_init(struct net *net, struct nlattr *nla,
 			   struct nlattr *est, struct tc_action **a, int ovr,
-			   int bind, bool rtnl_held,
+			   int bind, bool rtnl_held, struct tcf_proto *tp,
 			   struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, sample_net_id);
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index 902957beceb3..640ee5b785dc 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -80,7 +80,7 @@ static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = {
 static int tcf_simp_init(struct net *net, struct nlattr *nla,
 			 struct nlattr *est, struct tc_action **a,
 			 int ovr, int bind, bool rtnl_held,
-			 struct netlink_ext_ack *extack)
+			 struct tcf_proto *tp, struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, simp_net_id);
 	struct nlattr *tb[TCA_DEF_MAX + 1];
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
index 64dba3708fce..9fc8cfdd35b1 100644
--- a/net/sched/act_skbedit.c
+++ b/net/sched/act_skbedit.c
@@ -96,6 +96,7 @@ static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
 static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
 			    struct nlattr *est, struct tc_action **a,
 			    int ovr, int bind, bool rtnl_held,
+			    struct tcf_proto *tp,
 			    struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, skbedit_net_id);
diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c
index 59710a183bd3..35572d0e4576 100644
--- a/net/sched/act_skbmod.c
+++ b/net/sched/act_skbmod.c
@@ -82,6 +82,7 @@ static const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = {
 static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
 			   struct nlattr *est, struct tc_action **a,
 			   int ovr, int bind, bool rtnl_held,
+			   struct tcf_proto *tp,
 			   struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index 8b43fe0130f7..c4adc53e0fb4 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -209,6 +209,7 @@ static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
 static int tunnel_key_init(struct net *net, struct nlattr *nla,
 			   struct nlattr *est, struct tc_action **a,
 			   int ovr, int bind, bool rtnl_held,
+			   struct tcf_proto *tp,
 			   struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c
index 93fdaf707313..80fd0e238a10 100644
--- a/net/sched/act_vlan.c
+++ b/net/sched/act_vlan.c
@@ -105,7 +105,7 @@ static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
 static int tcf_vlan_init(struct net *net, struct nlattr *nla,
 			 struct nlattr *est, struct tc_action **a,
 			 int ovr, int bind, bool rtnl_held,
-			 struct netlink_ext_ack *extack)
+			 struct tcf_proto *tp, struct netlink_ext_ack *extack)
 {
 	struct tc_action_net *tn = net_generic(net, vlan_net_id);
 	struct nlattr *tb[TCA_VLAN_MAX + 1];
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH RFC 3/5] net/sched: act_bpf: validate the control action inside init()
  2019-02-15 23:06 [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Davide Caratti
  2019-02-15 23:06 ` [PATCH RFC 1/5] net/sched: fix refcount leak when 'goto_chain' is used Davide Caratti
  2019-02-15 23:06 ` [PATCH RFC 2/5] net/sched: prepare TC actions to properly validate the control action Davide Caratti
@ 2019-02-15 23:06 ` Davide Caratti
  2019-02-15 23:06 ` [PATCH RFC 4/5] net/sched: act_csum: " Davide Caratti
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Davide Caratti @ 2019-02-15 23:06 UTC (permalink / raw)
  To: Jamal Hadi Salim, Cong Wang, Jiri Pirko
  Cc: David S. Miller, Vlad Buslov, Paolo Abeni, netdev

Don't overwrite act_bpf data if the control control action is not valid,
to prevent loosing the previous configuration in case validation failed.
Not doing that caused NULL dereference in the data path if 'goto chain'
is used.

Tested with:
 # ./tdc.py -c bpf

Fixes: db50514f9a9c ("net: sched: add termination action to allow goto chain")
Fixes: 97763dc0f401 ("net_sched: reject unknown tcfa_action values")
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
---
 net/sched/act_bpf.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
index 88a729bdab25..e2c2ba5faeb3 100644
--- a/net/sched/act_bpf.c
+++ b/net/sched/act_bpf.c
@@ -17,6 +17,7 @@
 
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
 
 #include <linux/tc_act/tc_bpf.h>
 #include <net/tc_act/tc_bpf.h>
@@ -282,6 +283,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
 {
 	struct tc_action_net *tn = net_generic(net, bpf_net_id);
 	struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
+	struct tcf_chain *newchain = NULL;
 	struct tcf_bpf_cfg cfg, old;
 	struct tc_act_bpf *parm;
 	struct tcf_bpf *prog;
@@ -323,6 +325,10 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
 		return ret;
 	}
 
+	ret = tcf_action_check_ctrlact(parm->action, tp, &newchain, extack);
+	if (ret < 0)
+		goto out;
+
 	is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS];
 	is_ebpf = tb[TCA_ACT_BPF_FD];
 
@@ -350,7 +356,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
 	if (cfg.bpf_num_ops)
 		prog->bpf_num_ops = cfg.bpf_num_ops;
 
-	prog->tcf_action = parm->action;
+	tcf_action_set_ctrlact(*act, parm->action, newchain);
 	rcu_assign_pointer(prog->filter, cfg.filter);
 	spin_unlock_bh(&prog->tcf_lock);
 
@@ -364,6 +370,8 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
 
 	return res;
 out:
+	if (newchain)
+		tcf_chain_put_by_act(newchain);
 	tcf_idr_release(*act, bind);
 
 	return ret;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH RFC 4/5] net/sched: act_csum: validate the control action inside init()
  2019-02-15 23:06 [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Davide Caratti
                   ` (2 preceding siblings ...)
  2019-02-15 23:06 ` [PATCH RFC 3/5] net/sched: act_bpf: validate the control action inside init() Davide Caratti
@ 2019-02-15 23:06 ` Davide Caratti
  2019-02-15 23:06 ` [PATCH RFC 5/5] net/sched: act_gact: " Davide Caratti
  2019-02-19  6:42 ` [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Cong Wang
  5 siblings, 0 replies; 10+ messages in thread
From: Davide Caratti @ 2019-02-15 23:06 UTC (permalink / raw)
  To: Jamal Hadi Salim, Cong Wang, Jiri Pirko
  Cc: David S. Miller, Vlad Buslov, Paolo Abeni, netdev

Don't overwrite act_csum data if the control control action is not valid,
to prevent loosing the previous configuration in case validation failed.
Not doing that caused NULL dereference in the data path if 'goto chain'
is used.

Tested with:
 # ./tdc.py -c csum

Fixes: db50514f9a9c ("net: sched: add termination action to allow goto chain")
Fixes: 97763dc0f401 ("net_sched: reject unknown tcfa_action values")
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
---
 net/sched/act_csum.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index 1ae120c9ab02..bf0940156886 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -33,6 +33,7 @@
 #include <net/sctp/checksum.h>
 
 #include <net/act_api.h>
+#include <net/pkt_cls.h>
 
 #include <linux/tc_act/tc_csum.h>
 #include <net/tc_act/tc_csum.h>
@@ -52,6 +53,7 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
 	struct tc_action_net *tn = net_generic(net, csum_net_id);
 	struct tcf_csum_params *params_new;
 	struct nlattr *tb[TCA_CSUM_MAX + 1];
+	struct tcf_chain *newchain = NULL;
 	struct tc_csum *parm;
 	struct tcf_csum *p;
 	int ret = 0, err;
@@ -87,17 +89,23 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
 		return err;
 	}
 
+	err = tcf_action_check_ctrlact(parm->action, tp, &newchain, extack);
+	if (unlikely(err)) {
+		ret = err;
+		goto error;
+	}
+
 	p = to_tcf_csum(*a);
 
 	params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
 	if (unlikely(!params_new)) {
-		tcf_idr_release(*a, bind);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto error;
 	}
 	params_new->update_flags = parm->update_flags;
 
 	spin_lock_bh(&p->tcf_lock);
-	p->tcf_action = parm->action;
+	tcf_action_set_ctrlact(*a, parm->action, newchain);
 	rcu_swap_protected(p->params, params_new,
 			   lockdep_is_held(&p->tcf_lock));
 	spin_unlock_bh(&p->tcf_lock);
@@ -108,7 +116,13 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
 	if (ret == ACT_P_CREATED)
 		tcf_idr_insert(tn, *a);
 
+end:
 	return ret;
+error:
+	if (newchain)
+		tcf_chain_put_by_act(newchain);
+	tcf_idr_release(*a, bind);
+	goto end;
 }
 
 /**
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH RFC 5/5] net/sched: act_gact: validate the control action inside init()
  2019-02-15 23:06 [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Davide Caratti
                   ` (3 preceding siblings ...)
  2019-02-15 23:06 ` [PATCH RFC 4/5] net/sched: act_csum: " Davide Caratti
@ 2019-02-15 23:06 ` Davide Caratti
  2019-02-19  6:42 ` [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Cong Wang
  5 siblings, 0 replies; 10+ messages in thread
From: Davide Caratti @ 2019-02-15 23:06 UTC (permalink / raw)
  To: Jamal Hadi Salim, Cong Wang, Jiri Pirko
  Cc: David S. Miller, Vlad Buslov, Paolo Abeni, netdev

Don't overwrite act_gact data if the control control action is not valid,
to prevent loosing the previous configuration in case validation failed.
Not doing that caused NULL dereference in the data path if 'goto chain'
is used.

Tested with:
 # ./tdc.py -c gact

Fixes: db50514f9a9c ("net: sched: add termination action to allow goto chain")
Fixes: 97763dc0f401 ("net_sched: reject unknown tcfa_action values")
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
---
 net/sched/act_gact.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index 727bbca9534b..530e8bb8f94d 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -20,6 +20,7 @@
 #include <linux/init.h>
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
 #include <linux/tc_act/tc_gact.h>
 #include <net/tc_act/tc_gact.h>
 
@@ -61,6 +62,7 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
 {
 	struct tc_action_net *tn = net_generic(net, gact_net_id);
 	struct nlattr *tb[TCA_GACT_MAX + 1];
+	struct tcf_chain *newchain = NULL;
 	struct tc_gact *parm;
 	struct tcf_gact *gact;
 	int ret = 0;
@@ -116,10 +118,15 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
 		return err;
 	}
 
+	err = tcf_action_check_ctrlact(parm->action, tp, &newchain, extack);
+	if (unlikely(err)) {
+		ret = err;
+		goto error;
+	}
 	gact = to_gact(*a);
 
 	spin_lock_bh(&gact->tcf_lock);
-	gact->tcf_action = parm->action;
+	tcf_action_set_ctrlact(*a, parm->action, newchain);
 #ifdef CONFIG_GACT_PROB
 	if (p_parm) {
 		gact->tcfg_paction = p_parm->paction;
@@ -135,7 +142,13 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
 
 	if (ret == ACT_P_CREATED)
 		tcf_idr_insert(tn, *a);
+end:
 	return ret;
+error:
+	if (newchain)
+		tcf_chain_put_by_act(newchain);
+	tcf_idr_release(*a, bind);
+	goto end;
 }
 
 static int tcf_gact_act(struct sk_buff *skb, const struct tc_action *a,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC 2/5] net/sched: prepare TC actions to properly validate the control action
  2019-02-15 23:06 ` [PATCH RFC 2/5] net/sched: prepare TC actions to properly validate the control action Davide Caratti
@ 2019-02-18 16:52   ` Vlad Buslov
  2019-02-19 16:50     ` Davide Caratti
  0 siblings, 1 reply; 10+ messages in thread
From: Vlad Buslov @ 2019-02-18 16:52 UTC (permalink / raw)
  To: Davide Caratti
  Cc: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S. Miller,
	Paolo Abeni, netdev

Hi Davide,

I really like the idea of putting all action validation code in single
place, instead of spreading it between act API and specific actions init
code.

See my comment regarding minor problem with using
tcf_action_set_ctrlact() on current net-next below.

On Fri 15 Feb 2019 at 23:06, Davide Caratti <dcaratti@redhat.com> wrote:
> - add tcf_action_check_ctrlact(), and pass a pointer to struct tcf_proto
>   in each actions's init() function, to allow validation of 'goto chain'
>   control action.
> - add tcf_action_set_ctrlact(), to set the control action, release the
>   previous 'goto_chain' handle and replace it with the new one.
>
> Signed-off-by: Davide Caratti <dcaratti@redhat.com>
> ---
>  include/net/act_api.h      |  9 +++++-
>  net/sched/act_api.c        | 58 ++++++++++++++++++++++++++++++++++++--
>  net/sched/act_bpf.c        |  2 +-
>  net/sched/act_connmark.c   |  1 +
>  net/sched/act_csum.c       |  2 +-
>  net/sched/act_gact.c       |  2 +-
>  net/sched/act_ife.c        |  2 +-
>  net/sched/act_ipt.c        | 11 ++++----
>  net/sched/act_mirred.c     |  1 +
>  net/sched/act_nat.c        |  3 +-
>  net/sched/act_pedit.c      |  2 +-
>  net/sched/act_police.c     |  1 +
>  net/sched/act_sample.c     |  2 +-
>  net/sched/act_simple.c     |  2 +-
>  net/sched/act_skbedit.c    |  1 +
>  net/sched/act_skbmod.c     |  1 +
>  net/sched/act_tunnel_key.c |  1 +
>  net/sched/act_vlan.c       |  2 +-
>  18 files changed, 86 insertions(+), 17 deletions(-)
>
> diff --git a/include/net/act_api.h b/include/net/act_api.h
> index dbc795ec659e..55a8d44ac0c7 100644
> --- a/include/net/act_api.h
> +++ b/include/net/act_api.h
> @@ -90,7 +90,7 @@ struct tc_action_ops {
>  	int     (*lookup)(struct net *net, struct tc_action **a, u32 index);
>  	int     (*init)(struct net *net, struct nlattr *nla,
>  			struct nlattr *est, struct tc_action **act, int ovr,
> -			int bind, bool rtnl_held,
> +			int bind, bool rtnl_held, struct tcf_proto *tp,
>  			struct netlink_ext_ack *extack);
>  	int     (*walk)(struct net *, struct sk_buff *,
>  			struct netlink_callback *, int,
> @@ -181,6 +181,13 @@ int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
>  int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int);
>  int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int);
>
> +int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
> +			     struct tcf_chain **handle,
> +			     struct netlink_ext_ack *extack);
> +
> +void tcf_action_set_ctrlact(struct tc_action *p, int action,
> +			    struct tcf_chain *goto_chain);
> +
>  #endif /* CONFIG_NET_CLS_ACT */
>
>  static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes,
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index 91d79fac8cb2..088b0d846bde 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -71,6 +71,60 @@ static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
>  		call_rcu(&old->rcu, tcf_free_cookie_rcu);
>  }
>
> +int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
> +			     struct tcf_chain **handle,
> +			     struct netlink_ext_ack *extack)
> +{
> +	int opcode = TC_ACT_EXT_OPCODE(action), ret = -EINVAL;
> +	u32 chain_index;
> +
> +	if (!opcode)
> +		ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0;
> +	else if (opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC)
> +		ret = 0;
> +	if (ret) {
> +		NL_SET_ERR_MSG(extack, "invalid control action");
> +		goto end;
> +	}
> +
> +	if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) {
> +		chain_index = action & TC_ACT_EXT_VAL_MASK;
> +		if (!tp) {
> +			ret = -EINVAL;
> +			NL_SET_ERR_MSG(extack,
> +				       "can't use goto_chain with NULL proto");
> +			goto end;
> +		}
> +		if (!handle) {
> +			ret = -EINVAL;
> +			NL_SET_ERR_MSG(extack,
> +				       "can't put goto_chain on NULL handle");
> +			goto end;
> +		}
> +		*handle = tcf_chain_get_by_act(tp->chain->block, chain_index);
> +		if (!*handle) {
> +			ret = -ENOMEM;
> +			NL_SET_ERR_MSG(extack,
> +				       "can't allocate goto_chain handle");
> +		}
> +	}
> +end:
> +	return ret;
> +}
> +EXPORT_SYMBOL(tcf_action_check_ctrlact);
> +
> +void tcf_action_set_ctrlact(struct tc_action *p, int action,
> +			    struct tcf_chain *goto_chain)
> +{
> +	struct tcf_chain *old;
> +
> +	old = xchg(&p->goto_chain, goto_chain);
> +	if (old)
> +		tcf_chain_put_by_act(old);

This is blocking in current net-next because tcf_chain_put_by_act()
obtains block->lock mutex, so you can't call it while holding tcf_lock
spinlock like you do in following patches. Its not a big problem but I
guess you will have to just inline this code into all init handlers
instead of tcf_action_set_ctrlact() function.

BTW is atomic xchg strictly necessary if you hold tcf_lock while
re-assigning goto_chain pointer?

> +	p->tcfa_action = action;
> +}
> +EXPORT_SYMBOL(tcf_action_set_ctrlact);
> +
>  /* XXX: For standalone actions, we don't need a RCU grace period either, because
>   * actions are always connected to filters and filters are already destroyed in
>   * RCU callbacks, so after a RCU grace period actions are already disconnected
> @@ -890,10 +944,10 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  	/* backward compatibility for policer */
>  	if (name == NULL)
>  		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind,
> -				rtnl_held, extack);
> +				rtnl_held, tp, extack);
>  	else
>  		err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held,
> -				extack);
> +				tp, extack);
>  	if (err < 0)
>  		goto err_mod;
>
> diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
> index c7633843e223..88a729bdab25 100644
> --- a/net/sched/act_bpf.c
> +++ b/net/sched/act_bpf.c
> @@ -278,7 +278,7 @@ static void tcf_bpf_prog_fill_cfg(const struct tcf_bpf *prog,
>  static int tcf_bpf_init(struct net *net, struct nlattr *nla,
>  			struct nlattr *est, struct tc_action **act,
>  			int replace, int bind, bool rtnl_held,
> -			struct netlink_ext_ack *extack)
> +			struct tcf_proto *tp, struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, bpf_net_id);
>  	struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
> diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c
> index 8475913f2070..30c4c109c80c 100644
> --- a/net/sched/act_connmark.c
> +++ b/net/sched/act_connmark.c
> @@ -97,6 +97,7 @@ static const struct nla_policy connmark_policy[TCA_CONNMARK_MAX + 1] = {
>  static int tcf_connmark_init(struct net *net, struct nlattr *nla,
>  			     struct nlattr *est, struct tc_action **a,
>  			     int ovr, int bind, bool rtnl_held,
> +			     struct tcf_proto *tp,
>  			     struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, connmark_net_id);
> diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
> index 3dc25b7806d7..1ae120c9ab02 100644
> --- a/net/sched/act_csum.c
> +++ b/net/sched/act_csum.c
> @@ -46,7 +46,7 @@ static struct tc_action_ops act_csum_ops;
>
>  static int tcf_csum_init(struct net *net, struct nlattr *nla,
>  			 struct nlattr *est, struct tc_action **a, int ovr,
> -			 int bind, bool rtnl_held,
> +			 int bind, bool rtnl_held, struct tcf_proto *tp,
>  			 struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, csum_net_id);
> diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
> index b61c20ebb314..727bbca9534b 100644
> --- a/net/sched/act_gact.c
> +++ b/net/sched/act_gact.c
> @@ -57,7 +57,7 @@ static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = {
>  static int tcf_gact_init(struct net *net, struct nlattr *nla,
>  			 struct nlattr *est, struct tc_action **a,
>  			 int ovr, int bind, bool rtnl_held,
> -			 struct netlink_ext_ack *extack)
> +			 struct tcf_proto *tp, struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, gact_net_id);
>  	struct nlattr *tb[TCA_GACT_MAX + 1];
> diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
> index 30b63fa23ee2..9b2eb941e093 100644
> --- a/net/sched/act_ife.c
> +++ b/net/sched/act_ife.c
> @@ -469,7 +469,7 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
>  static int tcf_ife_init(struct net *net, struct nlattr *nla,
>  			struct nlattr *est, struct tc_action **a,
>  			int ovr, int bind, bool rtnl_held,
> -			struct netlink_ext_ack *extack)
> +			struct tcf_proto *tp, struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, ife_net_id);
>  	struct nlattr *tb[TCA_IFE_MAX + 1];
> diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
> index 8af6c11d2482..4b3c844ca988 100644
> --- a/net/sched/act_ipt.c
> +++ b/net/sched/act_ipt.c
> @@ -97,7 +97,8 @@ static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
>
>  static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
>  			  struct nlattr *est, struct tc_action **a,
> -			  const struct tc_action_ops *ops, int ovr, int bind)
> +			  const struct tc_action_ops *ops, int ovr, int bind,
> +			  struct tcf_proto *tp)
>  {
>  	struct tc_action_net *tn = net_generic(net, id);
>  	struct nlattr *tb[TCA_IPT_MAX + 1];
> @@ -206,20 +207,20 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
>
>  static int tcf_ipt_init(struct net *net, struct nlattr *nla,
>  			struct nlattr *est, struct tc_action **a, int ovr,
> -			int bind, bool rtnl_held,
> +			int bind, bool rtnl_held, struct tcf_proto *tp,
>  			struct netlink_ext_ack *extack)
>  {
>  	return __tcf_ipt_init(net, ipt_net_id, nla, est, a, &act_ipt_ops, ovr,
> -			      bind);
> +			      bind, tp);
>  }
>
>  static int tcf_xt_init(struct net *net, struct nlattr *nla,
>  		       struct nlattr *est, struct tc_action **a, int ovr,
> -		       int bind, bool unlocked,
> +		       int bind, bool unlocked, struct tcf_proto *tp,
>  		       struct netlink_ext_ack *extack)
>  {
>  	return __tcf_ipt_init(net, xt_net_id, nla, est, a, &act_xt_ops, ovr,
> -			      bind);
> +			      bind, tp);
>  }
>
>  static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a,
> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
> index c8cf4d10c435..69dda57f1097 100644
> --- a/net/sched/act_mirred.c
> +++ b/net/sched/act_mirred.c
> @@ -94,6 +94,7 @@ static struct tc_action_ops act_mirred_ops;
>  static int tcf_mirred_init(struct net *net, struct nlattr *nla,
>  			   struct nlattr *est, struct tc_action **a,
>  			   int ovr, int bind, bool rtnl_held,
> +			   struct tcf_proto *tp,
>  			   struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, mirred_net_id);
> diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
> index c5c1e23add77..526c4c99bcce 100644
> --- a/net/sched/act_nat.c
> +++ b/net/sched/act_nat.c
> @@ -38,7 +38,8 @@ static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
>
>  static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
>  			struct tc_action **a, int ovr, int bind,
> -			bool rtnl_held, struct netlink_ext_ack *extack)
> +			bool rtnl_held,	struct tcf_proto *tp,
> +			struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, nat_net_id);
>  	struct nlattr *tb[TCA_NAT_MAX + 1];
> diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
> index 2b372a06b432..1c7a0db7b466 100644
> --- a/net/sched/act_pedit.c
> +++ b/net/sched/act_pedit.c
> @@ -138,7 +138,7 @@ static int tcf_pedit_key_ex_dump(struct sk_buff *skb,
>  static int tcf_pedit_init(struct net *net, struct nlattr *nla,
>  			  struct nlattr *est, struct tc_action **a,
>  			  int ovr, int bind, bool rtnl_held,
> -			  struct netlink_ext_ack *extack)
> +			  struct tcf_proto *tp, struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, pedit_net_id);
>  	struct nlattr *tb[TCA_PEDIT_MAX + 1];
> diff --git a/net/sched/act_police.c b/net/sched/act_police.c
> index ec8ec55e0fe8..a444dd78a244 100644
> --- a/net/sched/act_police.c
> +++ b/net/sched/act_police.c
> @@ -83,6 +83,7 @@ static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
>  static int tcf_police_init(struct net *net, struct nlattr *nla,
>  			       struct nlattr *est, struct tc_action **a,
>  			       int ovr, int bind, bool rtnl_held,
> +			       struct tcf_proto *tp,
>  			       struct netlink_ext_ack *extack)
>  {
>  	int ret = 0, tcfp_result = TC_ACT_OK, err, size;
> diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c
> index 1a0c682fd734..b2154edcb535 100644
> --- a/net/sched/act_sample.c
> +++ b/net/sched/act_sample.c
> @@ -37,7 +37,7 @@ static const struct nla_policy sample_policy[TCA_SAMPLE_MAX + 1] = {
>
>  static int tcf_sample_init(struct net *net, struct nlattr *nla,
>  			   struct nlattr *est, struct tc_action **a, int ovr,
> -			   int bind, bool rtnl_held,
> +			   int bind, bool rtnl_held, struct tcf_proto *tp,
>  			   struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, sample_net_id);
> diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
> index 902957beceb3..640ee5b785dc 100644
> --- a/net/sched/act_simple.c
> +++ b/net/sched/act_simple.c
> @@ -80,7 +80,7 @@ static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = {
>  static int tcf_simp_init(struct net *net, struct nlattr *nla,
>  			 struct nlattr *est, struct tc_action **a,
>  			 int ovr, int bind, bool rtnl_held,
> -			 struct netlink_ext_ack *extack)
> +			 struct tcf_proto *tp, struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, simp_net_id);
>  	struct nlattr *tb[TCA_DEF_MAX + 1];
> diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
> index 64dba3708fce..9fc8cfdd35b1 100644
> --- a/net/sched/act_skbedit.c
> +++ b/net/sched/act_skbedit.c
> @@ -96,6 +96,7 @@ static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
>  static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
>  			    struct nlattr *est, struct tc_action **a,
>  			    int ovr, int bind, bool rtnl_held,
> +			    struct tcf_proto *tp,
>  			    struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, skbedit_net_id);
> diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c
> index 59710a183bd3..35572d0e4576 100644
> --- a/net/sched/act_skbmod.c
> +++ b/net/sched/act_skbmod.c
> @@ -82,6 +82,7 @@ static const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = {
>  static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
>  			   struct nlattr *est, struct tc_action **a,
>  			   int ovr, int bind, bool rtnl_held,
> +			   struct tcf_proto *tp,
>  			   struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
> diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
> index 8b43fe0130f7..c4adc53e0fb4 100644
> --- a/net/sched/act_tunnel_key.c
> +++ b/net/sched/act_tunnel_key.c
> @@ -209,6 +209,7 @@ static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
>  static int tunnel_key_init(struct net *net, struct nlattr *nla,
>  			   struct nlattr *est, struct tc_action **a,
>  			   int ovr, int bind, bool rtnl_held,
> +			   struct tcf_proto *tp,
>  			   struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);
> diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c
> index 93fdaf707313..80fd0e238a10 100644
> --- a/net/sched/act_vlan.c
> +++ b/net/sched/act_vlan.c
> @@ -105,7 +105,7 @@ static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
>  static int tcf_vlan_init(struct net *net, struct nlattr *nla,
>  			 struct nlattr *est, struct tc_action **a,
>  			 int ovr, int bind, bool rtnl_held,
> -			 struct netlink_ext_ack *extack)
> +			 struct tcf_proto *tp, struct netlink_ext_ack *extack)
>  {
>  	struct tc_action_net *tn = net_generic(net, vlan_net_id);
>  	struct nlattr *tb[TCA_VLAN_MAX + 1];

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters
  2019-02-15 23:06 [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Davide Caratti
                   ` (4 preceding siblings ...)
  2019-02-15 23:06 ` [PATCH RFC 5/5] net/sched: act_gact: " Davide Caratti
@ 2019-02-19  6:42 ` Cong Wang
  2019-02-19 16:51   ` Davide Caratti
  5 siblings, 1 reply; 10+ messages in thread
From: Cong Wang @ 2019-02-19  6:42 UTC (permalink / raw)
  To: Davide Caratti
  Cc: Jamal Hadi Salim, Jiri Pirko, David S. Miller, Vlad Buslov,
	Paolo Abeni, Linux Kernel Network Developers

On Fri, Feb 15, 2019 at 3:06 PM Davide Caratti <dcaratti@redhat.com> wrote:
>
> currently, the kernel checks for bad values of the control action in
> tcf_action_init_1(), after a successful call to the action's init()
> function. This causes three bad behaviors:

Yeah, I have been complaining about this for a long time,
although slightly differently. The problem here is the lack of
"copy" in RCU mechanism, which makes it nearly impossible
to rollback to the previous state of an action on failure path
of an update, which also makes RCU readers reading a partially
updated action too.

Before I fix the "copy" part, your fixes make sense to me. There
might be some other way to expose the action-specific tcfa_action
opcode, but it might not be better than yours.

BTW, please fold these bad behaviors into each appropriate
patch, and keep the cover letter as an overview of the whole
patchset rather than showing any details.

[...]

> all these three problems can be fixed if we validate the control action
> in the init() function, in the same way as we are already doing for all
> the other parameters.
>
> - patch 1 is a temporary fix for problem 2), but it's reverted at the
>   end of the series

Please drop patch 1, it is very unlikely only patch 1 will be backported,
I think the whole patchset should be, therefore we have no reason
to carry a temporary fix here.

Thanks.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC 2/5] net/sched: prepare TC actions to properly validate the control action
  2019-02-18 16:52   ` Vlad Buslov
@ 2019-02-19 16:50     ` Davide Caratti
  0 siblings, 0 replies; 10+ messages in thread
From: Davide Caratti @ 2019-02-19 16:50 UTC (permalink / raw)
  To: Vlad Buslov
  Cc: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S. Miller,
	Paolo Abeni, netdev

hi Vlad,

On Mon, 2019-02-18 at 16:52 +0000, Vlad Buslov wrote:
> Hi Davide,
> 
> I really like the idea of putting all action validation code in single
> place, instead of spreading it between act API and specific actions init
> code.

sure, the previous validation code was in tcf_action_add_1(), and I 
attempted to fix the problems (well, it's the same problem showing up in 
3 different ways) without adding the same code everywhere.

Sadly, we need to handle correctly the situations where  
tcf_action_check_ctrlact() returns an error, and I'm seeing that we
can't fix every action with the same exact code everywhere (to avoid
leaking IDRs, or memory, or both).

> See my comment regarding minor problem with using
> tcf_action_set_ctrlact() on current net-next below.


> > +void tcf_action_set_ctrlact(struct tc_action *p, int action,
> > +			    struct tcf_chain *goto_chain)
> > +{
> > +	struct tcf_chain *old;
> > +
> > +	old = xchg(&p->goto_chain, goto_chain);
> > +	if (old)
> > +		tcf_chain_put_by_act(old);
> 
> This is blocking in current net-next because tcf_chain_put_by_act()
> obtains block->lock mutex, so you can't call it while holding tcf_lock
> spinlock like you do in following patches. Its not a big problem but
> I guess you will have to just inline this code into all init handlers
> instead of tcf_action_set_ctrlact() function.

Argh! you are right. Thanks a lot for spotting this.

Ok, let's kill tcf_action_set_ctrlact() and swap 'newchain' with
a->goto_chain in each action init(), with a->tcfa_lock taken. I will
then call tcf_chain_put_by_act() in the init() handler, after the
spinlock is released.

> BTW is atomic xchg strictly necessary if you hold tcf_lock while
> re-assigning goto_chain pointer?

No, it's not necessary. The new value of goto_chain is used only when the
new value of tcfa_action is updated: so, if we just do the assignment of
goto_chain and tcfa_action together with the spinlock taken, at least we
are improving the 
current code.

Thanks!
-- 
davide



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters
  2019-02-19  6:42 ` [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Cong Wang
@ 2019-02-19 16:51   ` Davide Caratti
  0 siblings, 0 replies; 10+ messages in thread
From: Davide Caratti @ 2019-02-19 16:51 UTC (permalink / raw)
  To: Cong Wang
  Cc: Jamal Hadi Salim, Jiri Pirko, David S. Miller, Vlad Buslov,
	Paolo Abeni, Linux Kernel Network Developers

On Mon, 2019-02-18 at 22:42 -0800, Cong Wang wrote:
> On Fri, Feb 15, 2019 at 3:06 PM Davide Caratti <dcaratti@redhat.com> wrote:
> > currently, the kernel checks for bad values of the control action in
> > tcf_action_init_1(), after a successful call to the action's init()
> > function. This causes three bad behaviors:
> 
> Yeah, I have been complaining about this for a long time,
> although slightly differently. The problem here is the lack of
> "copy" in RCU mechanism, which makes it nearly impossible
> to rollback to the previous state of an action on failure path
> of an update, which also makes RCU readers reading a partially
> updated action too.

thanks for looking at this code.

by the way, I see that act_mirred has an error path after the
assignment of tcfm_eaction and tcfa_action, and this is again causing
a fail in the 'replace with bad action' tests ('half write', issue #1).
Since it's the same problem, I will fix this in the same patch (moving the
assignment after the 'if' test on the value of parm->ifindex.

> Before I fix the "copy" part, your fixes make sense to me. There
> might be some other way to expose the action-specific tcfa_action
> opcode, but it might not be better than yours.
> 
> BTW, please fold these bad behaviors into each appropriate
> patch, and keep the cover letter as an overview of the whole
> patchset rather than showing any details.
> 
> [...]

Ok, and I plan to add a selftest for each action - so that it's possible
to verify functionality (at least problem #1) before and after each
ommit.

> > all these three problems can be fixed if we validate the control action
> > in the init() function, in the same way as we are already doing for all
> > the other parameters.
> > 
> > - patch 1 is a temporary fix for problem 2), but it's reverted at the
> >   end of the series
> 
> Please drop patch 1, it is very unlikely only patch 1 will be backported,
> I think the whole patchset should be, therefore we have no reason
> to carry a temporary fix here.

sure, I will do that.
thanks!

-- 
davide



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-02-19 16:51 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-15 23:06 [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Davide Caratti
2019-02-15 23:06 ` [PATCH RFC 1/5] net/sched: fix refcount leak when 'goto_chain' is used Davide Caratti
2019-02-15 23:06 ` [PATCH RFC 2/5] net/sched: prepare TC actions to properly validate the control action Davide Caratti
2019-02-18 16:52   ` Vlad Buslov
2019-02-19 16:50     ` Davide Caratti
2019-02-15 23:06 ` [PATCH RFC 3/5] net/sched: act_bpf: validate the control action inside init() Davide Caratti
2019-02-15 23:06 ` [PATCH RFC 4/5] net/sched: act_csum: " Davide Caratti
2019-02-15 23:06 ` [PATCH RFC 5/5] net/sched: act_gact: " Davide Caratti
2019-02-19  6:42 ` [PATCH RFC 0/5] net/sched: validate the control action with all the other parameters Cong Wang
2019-02-19 16:51   ` Davide Caratti

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.