netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them
@ 2020-12-16 18:33 Davide Caratti
  2020-12-16 19:01 ` Vinicius Costa Gomes
  2020-12-17 19:05 ` Jakub Kicinski
  0 siblings, 2 replies; 7+ messages in thread
From: Davide Caratti @ 2020-12-16 18:33 UTC (permalink / raw)
  To: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S. Miller, Jakub Kicinski
  Cc: Vinicius Costa Gomes, netdev

syzkaller shows that packets can still be dequeued while taprio_destroy()
is running. Let sch_taprio use the reset() function to cancel the advance
timer and drop all skbs from the child qdiscs.

Fixes: 5a781ccbd19e ("tc: Add support for configuring the taprio scheduler")
Link: https://syzkaller.appspot.com/bug?id=f362872379bf8f0017fb667c1ab158f2d1e764ae
Reported-by: syzbot+8971da381fb5a31f542d@syzkaller.appspotmail.com
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
---
 net/sched/sch_taprio.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 26fb8a62996b..c74817ec9964 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -1597,6 +1597,21 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
 	return err;
 }
 
+static void taprio_reset(struct Qdisc *sch)
+{
+	struct taprio_sched *q = qdisc_priv(sch);
+	struct net_device *dev = qdisc_dev(sch);
+	int i;
+
+	hrtimer_cancel(&q->advance_timer);
+	if (q->qdiscs) {
+		for (i = 0; i < dev->num_tx_queues && q->qdiscs[i]; i++)
+			qdisc_reset(q->qdiscs[i]);
+	}
+	sch->qstats.backlog = 0;
+	sch->q.qlen = 0;
+}
+
 static void taprio_destroy(struct Qdisc *sch)
 {
 	struct taprio_sched *q = qdisc_priv(sch);
@@ -1607,7 +1622,6 @@ static void taprio_destroy(struct Qdisc *sch)
 	list_del(&q->taprio_list);
 	spin_unlock(&taprio_list_lock);
 
-	hrtimer_cancel(&q->advance_timer);
 
 	taprio_disable_offload(dev, q, NULL);
 
@@ -1954,6 +1968,7 @@ static struct Qdisc_ops taprio_qdisc_ops __read_mostly = {
 	.init		= taprio_init,
 	.change		= taprio_change,
 	.destroy	= taprio_destroy,
+	.reset		= taprio_reset,
 	.peek		= taprio_peek,
 	.dequeue	= taprio_dequeue,
 	.enqueue	= taprio_enqueue,
-- 
2.29.2


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

* Re: [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them
  2020-12-16 18:33 [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them Davide Caratti
@ 2020-12-16 19:01 ` Vinicius Costa Gomes
  2020-12-17 19:04   ` Jakub Kicinski
  2020-12-17 19:05 ` Jakub Kicinski
  1 sibling, 1 reply; 7+ messages in thread
From: Vinicius Costa Gomes @ 2020-12-16 19:01 UTC (permalink / raw)
  To: Davide Caratti, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	David S. Miller, Jakub Kicinski
  Cc: netdev

Davide Caratti <dcaratti@redhat.com> writes:

> syzkaller shows that packets can still be dequeued while taprio_destroy()
> is running. Let sch_taprio use the reset() function to cancel the advance
> timer and drop all skbs from the child qdiscs.
>
> Fixes: 5a781ccbd19e ("tc: Add support for configuring the taprio scheduler")
> Link: https://syzkaller.appspot.com/bug?id=f362872379bf8f0017fb667c1ab158f2d1e764ae
> Reported-by: syzbot+8971da381fb5a31f542d@syzkaller.appspotmail.com
> Signed-off-by: Davide Caratti <dcaratti@redhat.com>
> ---

Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>


Cheers,
-- 
Vinicius

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

* Re: [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them
  2020-12-16 19:01 ` Vinicius Costa Gomes
@ 2020-12-17 19:04   ` Jakub Kicinski
  0 siblings, 0 replies; 7+ messages in thread
From: Jakub Kicinski @ 2020-12-17 19:04 UTC (permalink / raw)
  To: Vinicius Costa Gomes
  Cc: Davide Caratti, Jamal Hadi Salim, Cong Wang, Jiri Pirko,
	David S. Miller, netdev

On Wed, 16 Dec 2020 11:01:54 -0800 Vinicius Costa Gomes wrote:
> Davide Caratti <dcaratti@redhat.com> writes:
> 
> > syzkaller shows that packets can still be dequeued while taprio_destroy()
> > is running. Let sch_taprio use the reset() function to cancel the advance
> > timer and drop all skbs from the child qdiscs.
> >
> > Fixes: 5a781ccbd19e ("tc: Add support for configuring the taprio scheduler")
> > Link: https://syzkaller.appspot.com/bug?id=f362872379bf8f0017fb667c1ab158f2d1e764ae
> > Reported-by: syzbot+8971da381fb5a31f542d@syzkaller.appspotmail.com
> > Signed-off-by: Davide Caratti <dcaratti@redhat.com>
>
> Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>

Applied thanks.

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

* Re: [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them
  2020-12-16 18:33 [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them Davide Caratti
  2020-12-16 19:01 ` Vinicius Costa Gomes
@ 2020-12-17 19:05 ` Jakub Kicinski
  2020-12-17 20:32   ` Davide Caratti
  1 sibling, 1 reply; 7+ messages in thread
From: Jakub Kicinski @ 2020-12-17 19:05 UTC (permalink / raw)
  To: Davide Caratti
  Cc: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S. Miller,
	Vinicius Costa Gomes, netdev

On Wed, 16 Dec 2020 19:33:29 +0100 Davide Caratti wrote:
> +	if (q->qdiscs) {
> +		for (i = 0; i < dev->num_tx_queues && q->qdiscs[i]; i++)
> +			qdisc_reset(q->qdiscs[i]);

Are you sure that we can't graft a NULL in the middle of the array?
Shouldn't this be:

	for (i = 0; i < dev->num_tx_queues; i++)
		if (q->qdiscs[i])
			qdisc_reset(q->qdiscs[i]);

?

If this is a problem it already exists in destroy so I'll apply anyway.

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

* Re: [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them
  2020-12-17 19:05 ` Jakub Kicinski
@ 2020-12-17 20:32   ` Davide Caratti
  2020-12-17 20:45     ` Jakub Kicinski
  0 siblings, 1 reply; 7+ messages in thread
From: Davide Caratti @ 2020-12-17 20:32 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S. Miller,
	Vinicius Costa Gomes, netdev

hello Jakub, and thanks for checking!

On Thu, 2020-12-17 at 11:05 -0800, Jakub Kicinski wrote:
> On Wed, 16 Dec 2020 19:33:29 +0100 Davide Caratti wrote:
> > +	if (q->qdiscs) {
> > +		for (i = 0; i < dev->num_tx_queues && q->qdiscs[i]; i++)
> > +			qdisc_reset(q->qdiscs[i]);
> 
> Are you sure that we can't graft a NULL in the middle of the array?

that should not happen, because child qdiscs are checked for being non-
NULL when they are created:

https://elixir.bootlin.com/linux/v5.10/source/net/sched/sch_taprio.c#L1674

and then assigned to q->qdiscs[i]. So, there might be NULL elements of
q->qdiscs[] in the middle of the array when taprio_reset() is called,
but it should be ok to finish the loop when we encounter the first one:
subsequent ones should be NULL as well.

> Shouldn't this be:
> 
> 	for (i = 0; i < dev->num_tx_queues; i++)
> 		if (q->qdiscs[i])
> 			qdisc_reset(q->qdiscs[i]);
> 
> ?

probably the above code is more robust, but - like you noticed - then we
should also change the same 'for' loop in taprio_destroy(), otherwise it
leaks resources. If you and Vinicius agree, I can post a follow-up patch
that makes ->reset() and ->destroy()  more consistent with ->enqueue() 
and ->dequeue(), and send it for net-next when it reopens. Ok?

-- 
davide


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

* Re: [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them
  2020-12-17 20:32   ` Davide Caratti
@ 2020-12-17 20:45     ` Jakub Kicinski
  2020-12-17 20:49       ` Davide Caratti
  0 siblings, 1 reply; 7+ messages in thread
From: Jakub Kicinski @ 2020-12-17 20:45 UTC (permalink / raw)
  To: Davide Caratti
  Cc: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S. Miller,
	Vinicius Costa Gomes, netdev

On Thu, 17 Dec 2020 21:32:29 +0100 Davide Caratti wrote:
> hello Jakub, and thanks for checking!
> 
> On Thu, 2020-12-17 at 11:05 -0800, Jakub Kicinski wrote:
> > On Wed, 16 Dec 2020 19:33:29 +0100 Davide Caratti wrote:  
> > > +	if (q->qdiscs) {
> > > +		for (i = 0; i < dev->num_tx_queues && q->qdiscs[i]; i++)
> > > +			qdisc_reset(q->qdiscs[i]);  
> > 
> > Are you sure that we can't graft a NULL in the middle of the array?  
> 
> that should not happen, because child qdiscs are checked for being non-
> NULL when they are created:
> 
> https://elixir.bootlin.com/linux/v5.10/source/net/sched/sch_taprio.c#L1674
> 
> and then assigned to q->qdiscs[i]. So, there might be NULL elements of
> q->qdiscs[] in the middle of the array when taprio_reset() is called,
> but it should be ok to finish the loop when we encounter the first one:
> subsequent ones should be NULL as well.

Right, but that's init, look at taprio_graft(). The child qdiscs can be
replaced at any time. And the replacement can be NULL otherwise why
would graft check "if (new)"


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

* Re: [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them
  2020-12-17 20:45     ` Jakub Kicinski
@ 2020-12-17 20:49       ` Davide Caratti
  0 siblings, 0 replies; 7+ messages in thread
From: Davide Caratti @ 2020-12-17 20:49 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Jamal Hadi Salim, Cong Wang, Jiri Pirko, David S. Miller,
	Vinicius Costa Gomes, netdev

On Thu, 2020-12-17 at 12:45 -0800, Jakub Kicinski wrote:
> Right, but that's init, look at taprio_graft(). The child qdiscs can be
> replaced at any time. And the replacement can be NULL otherwise why
> would graft check "if (new)"

good point, you are right. I'll send a follow-up patch right now.
thanks!

--  
davide


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

end of thread, other threads:[~2020-12-17 20:51 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-16 18:33 [PATCH net] net/sched: sch_taprio: reset child qdiscs before freeing them Davide Caratti
2020-12-16 19:01 ` Vinicius Costa Gomes
2020-12-17 19:04   ` Jakub Kicinski
2020-12-17 19:05 ` Jakub Kicinski
2020-12-17 20:32   ` Davide Caratti
2020-12-17 20:45     ` Jakub Kicinski
2020-12-17 20:49       ` Davide Caratti

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).