Re: [PATCH v6 6/6] blktrace: fix debugfs use after free
From: Christoph Hellwig
Date: Wed Jun 10 2020 - 02:43:18 EST
On Tue, Jun 09, 2020 at 05:53:59PM +0000, Luis Chamberlain wrote:
> > Feel free to add more comments, but please try to keep them short
> > and crisp. At the some point long comments really distract from what
> > is going on.
>
> Sure.
>
> Come to think of it, given the above, I think we can also do way with
> the the partition stuff too, and rely on the buts->name too. I'll try
> this out, and test it.
Yes, the sg path should work for partitions as well. That should not
only simplify the code, but also the comments, we can do something like
the full patch below (incorporating your original one). This doesn't
include the error check on the creation - I think that check probably
is a good idea for this case based on the comments in the old patch, but
also a separate issue that should go into another patch on top.
diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c
index 15df3a36e9fa43..a2800bc56fb4d3 100644
--- a/block/blk-mq-debugfs.c
+++ b/block/blk-mq-debugfs.c
@@ -824,9 +824,6 @@ void blk_mq_debugfs_register(struct request_queue *q)
struct blk_mq_hw_ctx *hctx;
int i;
- q->debugfs_dir = debugfs_create_dir(kobject_name(q->kobj.parent),
- blk_debugfs_root);
-
debugfs_create_files(q->debugfs_dir, q, blk_mq_debugfs_queue_attrs);
/*
@@ -857,9 +854,7 @@ void blk_mq_debugfs_register(struct request_queue *q)
void blk_mq_debugfs_unregister(struct request_queue *q)
{
- debugfs_remove_recursive(q->debugfs_dir);
q->sched_debugfs_dir = NULL;
- q->debugfs_dir = NULL;
}
static void blk_mq_debugfs_register_ctx(struct blk_mq_hw_ctx *hctx,
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 561624d4cc4e7f..4e9909e1b25736 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -11,6 +11,7 @@
#include <linux/blktrace_api.h>
#include <linux/blk-mq.h>
#include <linux/blk-cgroup.h>
+#include <linux/debugfs.h>
#include "blk.h"
#include "blk-mq.h"
@@ -918,6 +919,7 @@ static void blk_release_queue(struct kobject *kobj)
blk_trace_shutdown(q);
+ debugfs_remove_recursive(q->debugfs_dir);
if (queue_is_mq(q))
blk_mq_debugfs_unregister(q);
@@ -989,6 +991,9 @@ int blk_register_queue(struct gendisk *disk)
goto unlock;
}
+ q->debugfs_dir = debugfs_create_dir(kobject_name(q->kobj.parent),
+ blk_debugfs_root);
+
if (queue_is_mq(q)) {
__blk_mq_register_dev(dev, q);
blk_mq_debugfs_register(q);
diff --git a/block/blk.h b/block/blk.h
index b5d1f0fc6547c7..499308c6ab3b0f 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -14,9 +14,7 @@
/* Max future timer expiry for timeouts */
#define BLK_MAX_TIMEOUT (5 * HZ)
-#ifdef CONFIG_DEBUG_FS
extern struct dentry *blk_debugfs_root;
-#endif
struct blk_flush_queue {
unsigned int flush_pending_idx:1;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index fc88330a3d97ed..b49c7c741bc9f3 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -574,8 +574,8 @@ struct request_queue {
struct list_head tag_set_list;
struct bio_set bio_split;
-#ifdef CONFIG_BLK_DEBUG_FS
struct dentry *debugfs_dir;
+#ifdef CONFIG_BLK_DEBUG_FS
struct dentry *sched_debugfs_dir;
struct dentry *rqos_debugfs_dir;
#endif
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index fee5c8d8916690..6eb364b393714f 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -509,10 +509,15 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
if (!bt->msg_data)
goto err;
- ret = -ENOENT;
-
- dir = debugfs_lookup(buts->name, blk_debugfs_root);
- if (!dir)
+ /*
+ * When tracing a whole block device, reuse the existing debugfs
+ * directory created by the block layer. For partitions or character
+ * devices (e.g. /dev/sg), create a new debugfs directory that will be
+ * removed once the trace ends.
+ */
+ if (bdev && bdev == bdev->bd_contains)
+ dir = q->debugfs_dir;
+ else
bt->dir = dir = debugfs_create_dir(buts->name, blk_debugfs_root);
bt->dev = dev;
@@ -553,8 +558,6 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
ret = 0;
err:
- if (dir && !bt->dir)
- dput(dir);
if (ret)
blk_trace_free(bt);
return ret;