From: Eric Dumazet <hidden> Date: 2021-11-17 19:20:38
From: Eric Dumazet <edumazet@google.com>
RFC only, I yet have to convert some dev_put()/dev_hold() to show
how this works.
I am posting this because Jakub is working on same issue.
Eric Dumazet (2):
lib: add reference counting infrastructure
net: add dev_hold_track() and dev_put_track() helpers
include/linux/netdevice.h | 23 +++++++
include/linux/ref_tracker.h | 78 ++++++++++++++++++++++++
lib/Kconfig | 4 ++
lib/Makefile | 2 +
lib/ref_tracker.c | 116 ++++++++++++++++++++++++++++++++++++
net/Kconfig | 8 +++
net/core/dev.c | 3 +
7 files changed, 234 insertions(+)
create mode 100644 include/linux/ref_tracker.h
create mode 100644 lib/ref_tracker.c
--
2.34.0.rc1.387.gb447b232ab-goog
From: Eric Dumazet <hidden> Date: 2021-11-17 19:20:39
From: Eric Dumazet <edumazet@google.com>
It can be hard to track where references are taken and released.
In networking, we have annoying issues at device dismantles,
and we had various proposals to ease root causing them.
This patch adds new infrastructure pairing refcount increases
and decreases. This will self document code, because programmer
will have to associate increments/decrements.
This is controled by CONFIG_REF_TRACKER which can be selected
by users of this feature.
This adds both cpu and memory costs, and thus should be reserved
for debug kernel builds, or be enabled on demand with a static key.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/linux/ref_tracker.h | 78 ++++++++++++++++++++++++
lib/Kconfig | 4 ++
lib/Makefile | 2 +
lib/ref_tracker.c | 116 ++++++++++++++++++++++++++++++++++++
4 files changed, 200 insertions(+)
create mode 100644 include/linux/ref_tracker.h
create mode 100644 lib/ref_tracker.c
@@ -0,0 +1,78 @@+// SPDX-License-Identifier: GPL-2.0-or-later+#ifndef _LINUX_REF_TRACKER_H+#define _LINUX_REF_TRACKER_H+#include<linux/types.h>+#include<linux/spinlock.h>+#include<linux/stackdepot.h>++structref_tracker{+#ifdef CONFIG_REF_TRACKER+structlist_headhead;/* anchor into dir->list or dir->quarantine */+booldead;+depot_stack_handle_talloc_stack_handle;+depot_stack_handle_tfree_stack_handle;+#endif+};++structref_tracker_dir{+#ifdef CONFIG_REF_TRACKER+spinlock_tlock;+unsignedintquarantine_avail;+structlist_headlist;/* List of active trackers */+structlist_headquarantine;/* List of dead trackers */+#endif+};++#ifdef CONFIG_REF_TRACKER+staticinlinevoidref_tracker_dir_init(structref_tracker_dir*dir,+unsignedintquarantine_count)+{+INIT_LIST_HEAD(&dir->list);+INIT_LIST_HEAD(&dir->quarantine);+spin_lock_init(&dir->lock);+dir->quarantine_avail=quarantine_count;+}++voidref_tracker_dir_exit(structref_tracker_dir*dir);++voidref_tracker_dir_print(structref_tracker_dir*dir,+unsignedintdisplay_limit);++intref_tracker_alloc(structref_tracker_dir*dir,+structref_tracker**trackerp,gfp_tgfp);++intref_tracker_free(structref_tracker_dir*dir,+structref_tracker**trackerp);++#else /* CONFIG_REF_TRACKER */++staticinlinevoidref_tracker_dir_init(structref_tracker_dir*dir,+unsignedintquarantine_count)+{+}++staticinlinevoidref_tracker_dir_exit(structref_tracker_dir*dir)+{+}++staticinlinevoidref_tracker_dir_print(structref_tracker_dir*dir,+unsignedintdisplay_limit)+{+}++staticinlineintref_tracker_alloc(structref_tracker_dir*dir,+structref_tracker**trackerp,+gfp_tgfp)+{+return0;+}++staticinlineintref_tracker_free(structref_tracker_dir*dir,+structref_tracker**trackerp)+{+return0;+}++#endif++#endif /* _LINUX_REF_TRACKER_H */
From: Eric Dumazet <hidden> Date: 2021-11-17 19:20:44
From: Eric Dumazet <edumazet@google.com>
They should replace dev_hold() and dev_put().
To use these helpers, each data structure owning a refcount
should also use a "struct ref_tracker" to pair the hold and put.
Whenever a leak happens, we will get precise stack traces
of the point dev_hold_track() happened, at device dismantle phase.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/linux/netdevice.h | 23 +++++++++++++++++++++++
net/Kconfig | 8 ++++++++
net/core/dev.c | 3 +++
3 files changed, 34 insertions(+)
From: Jakub Kicinski <kuba@kernel.org> Date: 2021-11-17 20:03:51
On Wed, 17 Nov 2021 11:20:30 -0800 Eric Dumazet wrote:
From: Eric Dumazet <edumazet@google.com>
It can be hard to track where references are taken and released.
In networking, we have annoying issues at device dismantles,
and we had various proposals to ease root causing them.
This patch adds new infrastructure pairing refcount increases
and decreases. This will self document code, because programmer
will have to associate increments/decrements.
This is controled by CONFIG_REF_TRACKER which can be selected
by users of this feature.
This adds both cpu and memory costs, and thus should be reserved
for debug kernel builds, or be enabled on demand with a static key.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Looks great, this is what I had in mind when I said:
| In the future we can extend this structure to also catch those
| who fail to release the ref on unregistering notification.
I realized today we can get quite a lot of coverage by just plugging
in object debug infra.
The main differences I see:
- do we ever want to use this in prod? - if not why allocate the
tracker itself dynamically? The double pointer interface seems
harder to compile out completely
- whether one stored netdev ptr can hold multiple refs
- do we want to wrap the pointer itself or have the "tracker" object
be a separate entity
- do we want to catch "use after free" when ref is accessed after
it was already released
No strong preference either way.
From: Eric Dumazet <edumazet@google.com> Date: 2021-11-17 20:16:32
On Wed, Nov 17, 2021 at 12:03 PM Jakub Kicinski [off-list ref] wrote:
On Wed, 17 Nov 2021 11:20:30 -0800 Eric Dumazet wrote:
quoted
From: Eric Dumazet <edumazet@google.com>
It can be hard to track where references are taken and released.
In networking, we have annoying issues at device dismantles,
and we had various proposals to ease root causing them.
This patch adds new infrastructure pairing refcount increases
and decreases. This will self document code, because programmer
will have to associate increments/decrements.
This is controled by CONFIG_REF_TRACKER which can be selected
by users of this feature.
This adds both cpu and memory costs, and thus should be reserved
for debug kernel builds, or be enabled on demand with a static key.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Looks great, this is what I had in mind when I said:
| In the future we can extend this structure to also catch those
| who fail to release the ref on unregistering notification.
I realized today we can get quite a lot of coverage by just plugging
in object debug infra.
The main differences I see:
- do we ever want to use this in prod? - if not why allocate the
tracker itself dynamically? The double pointer interface seems
harder to compile out completely
I think that maintaining the tracking state in separate storage would
detect cases
where the object has been freed, without the help of KASAN.
- whether one stored netdev ptr can hold multiple refs
For a same stack depot then ?
Problem is that at the time of dev_hold(), we do not know if
there is one associated dev_put() or multiple ones (different stack depot)
- do we want to wrap the pointer itself or have the "tracker" object
be a separate entity
- do we want to catch "use after free" when ref is accessed after
it was already released
No strong preference either way.
BTW my current suspicion about reported leaks is in
rt6_uncached_list_flush_dev()
I was considering something like
From: Jakub Kicinski <kuba@kernel.org> Date: 2021-11-17 20:47:16
On Wed, 17 Nov 2021 12:16:15 -0800 Eric Dumazet wrote:
On Wed, Nov 17, 2021 at 12:03 PM Jakub Kicinski [off-list ref] wrote:
quoted
Looks great, this is what I had in mind when I said:
| In the future we can extend this structure to also catch those
| who fail to release the ref on unregistering notification.
I realized today we can get quite a lot of coverage by just plugging
in object debug infra.
The main differences I see:
- do we ever want to use this in prod? - if not why allocate the
tracker itself dynamically? The double pointer interface seems
harder to compile out completely
I think that maintaining the tracking state in separate storage would
detect cases where the object has been freed, without the help of KASAN.
Makes sense, I guess we can hang more of the information of a secondary
object?
Maybe I'm missing a trick on how to make the feature consume no space
when disabled via Kconfig.
quoted
- whether one stored netdev ptr can hold multiple refs
For a same stack depot then ?
Not necessarily.
Problem is that at the time of dev_hold(), we do not know if
there is one associated dev_put() or multiple ones (different stack depot)
Ack. My thinking was hold all stacks until tracker is completely
drained of refs. We'd have to collect both hold and put stacks in
that case and if ref leak happens try to match them up manually
later (manually == human).
But if we can get away without allowing multiple refs with one tracker
that makes life easier, and is probably a cleaner API, anyway.
quoted hunk
quoted
- do we want to wrap the pointer itself or have the "tracker" object
be a separate entity
- do we want to catch "use after free" when ref is accessed after
it was already released
No strong preference either way.
BTW my current suspicion about reported leaks is in
rt6_uncached_list_flush_dev()
I was considering something like
From: Eric Dumazet <hidden> Date: 2021-11-17 22:43:29
On 11/17/21 12:47 PM, Jakub Kicinski wrote:
On Wed, 17 Nov 2021 12:16:15 -0800 Eric Dumazet wrote:
quoted
On Wed, Nov 17, 2021 at 12:03 PM Jakub Kicinski [off-list ref] wrote:
quoted
Looks great, this is what I had in mind when I said:
| In the future we can extend this structure to also catch those
| who fail to release the ref on unregistering notification.
I realized today we can get quite a lot of coverage by just plugging
in object debug infra.
The main differences I see:
- do we ever want to use this in prod? - if not why allocate the
tracker itself dynamically? The double pointer interface seems
harder to compile out completely
I think that maintaining the tracking state in separate storage would
detect cases where the object has been freed, without the help of KASAN.
Makes sense, I guess we can hang more of the information of a secondary
object?
Maybe I'm missing a trick on how to make the feature consume no space
when disabled via Kconfig.
If not enabled in Kconfig, the structures are empty, so consume no space.
Basically this should a nop.
From: Jakub Kicinski <kuba@kernel.org> Date: 2021-11-18 00:26:31
On Wed, 17 Nov 2021 14:43:24 -0800 Eric Dumazet wrote:
On 11/17/21 12:47 PM, Jakub Kicinski wrote:
quoted
On Wed, 17 Nov 2021 12:16:15 -0800 Eric Dumazet wrote:
quoted
I think that maintaining the tracking state in separate storage would
detect cases where the object has been freed, without the help of KASAN.
Makes sense, I guess we can hang more of the information of a secondary
object?
Maybe I'm missing a trick on how to make the feature consume no space
when disabled via Kconfig.
If not enabled in Kconfig, the structures are empty, so consume no space.
Basically this should a nop.
Right, probably not worth going back and forth, example use will clarify
this.
I feel like the two approaches are somewhat complementary, object debug
can help us pin point where ref got freed / lost. Could be useful if
there are many release paths for the same struct.
How do you feel about the struct netdev_ref wrapper I made? Do you
prefer to keep the tracking independent or can we provide the sort of
API I had in mind as well as yours:
void netdev_hold(struct netdev_ref *ref, struct net_device *dev)
void netdev_put(struct netdev_ref *ref)
struct net_device *netdev_ref_ptr(const struct netdev_ref *ref)
(doing both your tracking and object debug behind the scenes)
Hi Eric, Jakub,
How strongly do you want to make this work w/o KASAN?
I am asking because KASAN will already memorize alloc/free stacks for every
heap object (+ pids + 2 aux stacks with kasan_record_aux_stack()).
So basically we just need to alloc struct list_head and won't need
quarantine/quarantine_avail in ref_tracker_dir.
If there are some refcount bugs, it may be due to a previous use-after-free,
so debugging a refcount bug w/o KASAN may be waste of time.
This may benefit from __GFP_NOFAIL. syzkaller will use fault injection to fail
this. And I think it will do more bad than good.
We could also note this condition in dir, along the lines of:
if (!tracker) {
dir->failed = true;
To print on any errors and to check in ref_tracker_free():
int ref_tracker_free(struct ref_tracker_dir *dir,
struct ref_tracker **trackerp)
{
...
if (!tracker) {
WARN_ON(!dir->failed);
return -EEXIST;
}
This would be a bug, right?
Or:
*trackerp = tracker = kzalloc(sizeof(*tracker), gfp);
if (!tracker) {
*tracker = TRACKERP_ALLOC_FAILED;
return -ENOMEM;
}
and then check TRACKERP_ALLOC_FAILED in ref_tracker_free().
dev_hold_track() ignores the return value, so it would be useful to note
this condition.
+ if (tracker->dead) {
+ pr_err("reference already released.\n");
This and other custom prints won't be detected as bugs by syzkaller and other
testing systems, they detect the standard BUG/WARNING. Please use these.
ref_tracker_free() uses unnecesary long critical sections. I understand this
is debugging code, but frequently debugging code is so pessimistic that nobody
use it. If we enable this on syzbot, it will also slowdown all fuzzing.
I think with just a small code shuffling critical sections can be
significantly reduced:
nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
tracker->free_stack_handle = stack_depot_save(entries, nr_entries, GFP_ATOMIC);
spin_lock_irqsave(&dir->lock, flags);
if (tracker->dead)
...
tracker->dead = true;
list_move_tail(&tracker->head, &dir->quarantine);
if (!dir->quarantine_avail) {
tracker = list_first_entry(&dir->quarantine, struct ref_tracker, head);
list_del(&tracker->head);
} else {
dir->quarantine_avail--;
tracker = NULL;
}
spin_unlock_irqrestore(&dir->lock, flags);
kfree(tracker);
The saved stacks can be longer because they are de-duped. But stacks insered
into stack_depot need to be trimmed with filter_irq_stacks(). It seems that
almost all current users got it wrong. We are considering moving
filter_irq_stacks() into stack_depot_save(), but it's not done yet.
From: Jakub Kicinski <kuba@kernel.org> Date: 2021-11-30 15:03:57
On Tue, 30 Nov 2021 10:09:52 +0100 Dmitry Vyukov wrote:
Hi Eric, Jakub,
How strongly do you want to make this work w/o KASAN?
I am asking because KASAN will already memorize alloc/free stacks for every
heap object (+ pids + 2 aux stacks with kasan_record_aux_stack()).
So basically we just need to alloc struct list_head and won't need
quarantine/quarantine_avail in ref_tracker_dir.
If there are some refcount bugs, it may be due to a previous use-after-free,
so debugging a refcount bug w/o KASAN may be waste of time.
I don't mind, I was primarily targeting syzbot instances which will
have KASAN enabled AFAIU.
From: Eric Dumazet <edumazet@google.com> Date: 2021-11-30 15:14:46
On Tue, Nov 30, 2021 at 1:09 AM Dmitry Vyukov [off-list ref] wrote:
Hi Eric, Jakub,
How strongly do you want to make this work w/o KASAN?
I am asking because KASAN will already memorize alloc/free stacks for every
heap object (+ pids + 2 aux stacks with kasan_record_aux_stack()).
So basically we just need to alloc struct list_head and won't need
quarantine/quarantine_avail in ref_tracker_dir.
If there are some refcount bugs, it may be due to a previous use-after-free,
so debugging a refcount bug w/o KASAN may be waste of time.
No strong opinion, we could have the quarantine stuff enabled only if
KASAN is not compiled in.
I was trying to make something that could be used even in a production
environment, for seldom modified refcounts.
As this tracking is optional, we do not have to use it in very small
sections of code, where the inc/dec are happening in obviously correct
and not long living pairs.
On Tue, 30 Nov 2021 at 16:08, Eric Dumazet [off-list ref] wrote:
quoted
Hi Eric, Jakub,
How strongly do you want to make this work w/o KASAN?
I am asking because KASAN will already memorize alloc/free stacks for every
heap object (+ pids + 2 aux stacks with kasan_record_aux_stack()).
So basically we just need to alloc struct list_head and won't need
quarantine/quarantine_avail in ref_tracker_dir.
If there are some refcount bugs, it may be due to a previous use-after-free,
so debugging a refcount bug w/o KASAN may be waste of time.
No strong opinion, we could have the quarantine stuff enabled only if
KASAN is not compiled in.
I was trying to make something that could be used even in a production
environment, for seldom modified refcounts.
As this tracking is optional, we do not have to use it in very small
sections of code, where the inc/dec are happening in obviously correct
and not long living pairs.
If it won't be used on very frequent paths, then it probably does not
matter much for syzbot as well. And additional ifdefs are not worth
it. Then try to go with your current version.