Message ID | 63b6d79b0e830ebb0283e020db4df3cdfdfb2b94.1608142843.git.dcaratti@redhat.com (mailing list archive) |
---|---|
State | Accepted |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | [net] net/sched: sch_taprio: reset child qdiscs before freeing them | expand |
Context | Check | Description |
---|---|---|
netdev/cover_letter | success | Link |
netdev/fixes_present | success | Link |
netdev/patch_count | success | Link |
netdev/tree_selection | success | Clearly marked for net |
netdev/subject_prefix | success | Link |
netdev/source_inline | success | Was 0 now: 0 |
netdev/verify_signedoff | success | Link |
netdev/module_param | success | Was 0 now: 0 |
netdev/build_32bit | success | Errors and warnings before: 2 this patch: 2 |
netdev/kdoc | success | Errors and warnings before: 0 this patch: 0 |
netdev/verify_fixes | success | Link |
netdev/checkpatch | success | total: 0 errors, 0 warnings, 0 checks, 35 lines checked |
netdev/build_allmodconfig_warn | success | Errors and warnings before: 2 this patch: 2 |
netdev/header_inline | success | Link |
netdev/stable | success | Stable not CCed |
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,
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.
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.
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?
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)"
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
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,
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(-)