block: fix blktrace debugfs use after free
Subsystems (8)
v4 cover block: fix blktrace debugfs use after free
Phew, well, since we did't hear back about removing scsi-generic blktrace functionality I put work into addressing to keep it. That took a lot of code inspection and also testing. Since libvirt is limited to what devices you can test I resorted to testing all supported device types with iscsi tcp and tgt.
I decided to simplfiy the partition work further by just using a symbolic link. In the end that makes the blktrace code even cleaner than anything we had before.
scsi-generic stuff still required quite a bit of work to figure out what to do. Since scsi devices probe asynchronously and scsi-generic is nothing but a class_interface whose sg_add_device() runs *prior* to the scsi device probe, we currently address the symlink on the sg ioctl. I however think this reveals a shortcoming of the class interface, now that we have async probe and its used widely. I think we need a probe_complete() call or something like that. If that seems reasonable I can work on that, that would allow us to move the debugfs_dir symlink / settings from sg's ioctl to a new proper call. I'd prefer to address that later though, as an evolution.
Its my first time touching scsi stuff, so I'd highly appreciate a good review of what I propose for scsi-generic. It gets a bit more complicated with some drivers using the bsg queue. FWIW, if bsg is enabled we *reshare* the request_queue from the scsi_device, unless you're a scsi transport, in which case it creates its own.
You can find this on my git tree:
https://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux-next.git/log/?h=20200508-block-fixes
Luis Chamberlain (5):
block: revert back to synchronous request_queue removal
block: move main block debugfs initialization to its own file
blktrace: fix debugfs use after free
blktrace: break out of blktrace setup on concurrent calls
loop: be paranoid on exit and prevent new additions / removals
block/Makefile | 1 + block/blk-core.c | 32 ++++-- block/blk-debugfs.c | 202 +++++++++++++++++++++++++++++++++++ block/blk-mq-debugfs.c | 5 - block/blk-sysfs.c | 46 ++++---- block/blk.h | 23 ++++ block/bsg.c | 2 + block/genhd.c | 73 ++++++++++++- block/partitions/core.c | 9 ++ drivers/block/loop.c | 4 + drivers/scsi/ch.c | 1 + drivers/scsi/sg.c | 75 +++++++++++++ drivers/scsi/st.c | 2 + include/linux/blkdev.h | 6 +- include/linux/blktrace_api.h | 1 - include/linux/genhd.h | 69 ++++++++++++ kernel/trace/blktrace.c | 33 ++++-- 17 files changed, 539 insertions(+), 45 deletions(-) create mode 100644 block/blk-debugfs.c
Signature · 1 line
2.25.1
PATCH v4 1/5 block: revert back to synchronous request_queue removal
Commit dc9edc44de6c ("block: Fix a blk_exit_rl() regression") merged on v4.12 moved the work behind blk_release_queue() into a workqueue after a splat floated around which indicated some work on blk_release_queue() could sleep in blk_exit_rl(). This splat would be possible when a driver called blk_put_queue() or blk_cleanup_queue() (which calls blk_put_queue() as its final call) from an atomic context.
blk_put_queue() decrements the refcount for the request_queue kobject, and upon reaching 0 blk_release_queue() is called. Although blk_exit_rl() is now removed through commit db6d9952356 ("block: remove request_list code") on v5.0, we reserve the right to be able to sleep within blk_release_queue() context.
The last reference for the request_queue must not be called from atomic context. *When* the last reference to the request_queue reaches 0 varies, and so let's take the opportunity to document when that is expected to happen and also document the context of the related calls as best as possible so we can avoid future issues, and with the hopes that the synchronous request_queue removal sticks.
We revert back to synchronous request_queue removal because asynchronous removal creates a regression with expected userspace interaction with several drivers. An example is when removing the loopback driver, one uses ioctls from userspace to do so, but upon return and if successful, one expects the device to be removed. Likewise if one races to add another device the new one may not be added as it is still being removed. This was expected behavior before and it now fails as the device is still present and busy still. Moving to asynchronous request_queue removal could have broken many scripts which relied on the removal to have been completed if there was no error. Document this expectation as well so that this doesn't regress userspace again.
Using asynchronous request_queue removal however has helped us find other bugs. In the future we can test what could break with this arrangement by enabling CONFIG_DEBUG_KOBJECT_RELEASE.
--- block/blk-core.c | 23 +++++++++++++ block/blk-sysfs.c | 43 +++++++++++++------------ block/genhd.c | 73 +++++++++++++++++++++++++++++++++++++++++- include/linux/blkdev.h | 2 -- 4 files changed, 117 insertions(+), 24 deletions(-)
block/blk-core.c +23 -0
@@ -306,6 +306,16 @@ void blk_clear_pm_only(struct request_queue *q) }EXPORT_SYMBOL_GPL (blk_clear_pm_only); +/** +* blk_put_queue - decrement the request_queue refcount +* @q: the request_queue structure to decrement the refcount for +* +* Decrements the refcount of the request_queue kobject. When this reaches 0 +* we'll have blk_release_queue() called. +* +* Context: Any context, but the last reference must not be dropped from +* atomic context. +*/ void blk_put_queue (struct request_queue *q) {kobject_put (&q->kobj);
@@ -337,9 +347,14 @@ EXPORT_SYMBOL_GPL(blk_set_queue_dying);* * Mark @q DYING, drain all pending requests, mark @q DEAD, destroy and * put it. All future requests will be failed immediately with -ENODEV. +* +* Context: can sleep */ void blk_cleanup_queue (struct request_queue *q) { +/* cannot be called from atomic context */ +might_sleep (); +WARN_ON_ONCE (blk_queue_registered (q));/* mark @q DYING, no new request or merges will be allowed afterwards */
@@ -584,6 +599,14 @@ struct request_queue *blk_alloc_queue(make_request_fn make_request, int node_id) }EXPORT_SYMBOL (blk_alloc_queue); +/** +* blk_get_queue - increment the request_queue refcount +* @q: the request_queue structure to increment the refcount for +* +* Increment the refcount of the request_queue kobject. +* +* Context: Any context. +*/ bool blk_get_queue (struct request_queue *q) {if (likely (!blk_queue_dying (q))) {
block/blk-sysfs.c +22 -21
59 lines not on this page · raw message
block/genhd.c +72 -1
125 lines not on this page · raw message
include/linux/blkdev.h +0 -2
@@ -571,8 +571,6 @@ struct request_queue {size_t cmd_size; -struct work_struct release_work; -#define BLK_MAX_WRITE_HINTS 5u64 write_hints[BLK_MAX_WRITE_HINTS ]; };
Signature · 1 line
2.25.1
Re: [PATCH v4 1/5] block: revert back to synchronous request_queue removal
Quoted 11 lines · Luis Chamberlain
We revert back to synchronous
request_queueremoval because asynchronous removal creates a regression with expected userspace interaction with several drivers. An example is when removing the loopback driver, one uses ioctls from userspace to do so, but upon return and if successful, one expects the device to be removed. Likewise if one races to add another device the new one may not be added as it is still being removed. This was expected behavior before and it now fails as the device is still present and busy still. Moving to asynchronousrequest_queueremoval could have broken many scripts which relied on the removal to have been completed if there was no error. Document this expectation as well so that this doesn't regress userspace again.
Re: [PATCH v4 1/5] block: revert back to synchronous request_queue removal
In reply to Luis ChamberlainQuoted 32 lines · Luis Chamberlain
Commit
dc9edc44de6c("block: Fix ablk_exit_rl()regression") merged on v4.12 moved the work behindblk_release_queue()into a workqueue after a splat floated around which indicated some work onblk_release_queue()could sleep inblk_exit_rl(). This splat would be possible when a driver calledblk_put_queue()orblk_cleanup_queue()(which callsblk_put_queue()as its final call) from an atomic context.
blk_put_queue()decrements the refcount for therequest_queuekobject, and upon reaching 0blk_release_queue()is called. Althoughblk_exit_rl()is now removed through commitdb6d9952356("block: removerequest_listcode") on v5.0, we reserve the right to be able to sleep withinblk_release_queue()context.The last reference for the
request_queuemust not be called from atomic context. *When* the last reference to therequest_queuereaches 0 varies, and so let's take the opportunity to document when that is expected to happen and also document the context of the related calls as best as possible so we can avoid future issues, and with the hopes that the synchronousrequest_queueremoval sticks.We revert back to synchronous
request_queueremoval because asynchronous removal creates a regression with expected userspace interaction with several drivers. An example is when removing the loopback driver, one uses ioctls from userspace to do so, but upon return and if successful, one expects the device to be removed. Likewise if one races to add another device the new one may not be added as it is still being removed. This was expected behavior before and it now fails as the device is still present and busy still. Moving to asynchronousrequest_queueremoval could have broken many scripts which relied on the removal to have been completed if there was no error. Document this expectation as well so that this doesn't regress userspace again.
Quoted 3 lines
Using asynchronous
request_queueremoval however has helped us find other bugs. In the future we can test what could break with this arrangement by enablingCONFIG_DEBUG_KOBJECT_RELEASE.
You are adding documenation and might_sleep() calls all over the place in here, making the "real" change in the patch hard to pick out.
How about you split this up into 3 patches, one for documentation, one for might_sleep() and one for the real change? Or maybe just 2 patches, but what you have here seems excessive.
thanks,
greg k-h
Re: [PATCH v4 1/5] block: revert back to synchronous request_queue removal
Quoted 37 lines · Greg KH
Quoted 32 lines · Luis Chamberlain
Commit
dc9edc44de6c("block: Fix ablk_exit_rl()regression") merged on v4.12 moved the work behindblk_release_queue()into a workqueue after a splat floated around which indicated some work onblk_release_queue()could sleep inblk_exit_rl(). This splat would be possible when a driver calledblk_put_queue()orblk_cleanup_queue()(which callsblk_put_queue()as its final call) from an atomic context.
blk_put_queue()decrements the refcount for therequest_queuekobject, and upon reaching 0blk_release_queue()is called. Althoughblk_exit_rl()is now removed through commitdb6d9952356("block: removerequest_listcode") on v5.0, we reserve the right to be able to sleep withinblk_release_queue()context.The last reference for the
request_queuemust not be called from atomic context. *When* the last reference to therequest_queuereaches 0 varies, and so let's take the opportunity to document when that is expected to happen and also document the context of the related calls as best as possible so we can avoid future issues, and with the hopes that the synchronousrequest_queueremoval sticks.We revert back to synchronous
request_queueremoval because asynchronous removal creates a regression with expected userspace interaction with several drivers. An example is when removing the loopback driver, one uses ioctls from userspace to do so, but upon return and if successful, one expects the device to be removed. Likewise if one races to add another device the new one may not be added as it is still being removed. This was expected behavior before and it now fails as the device is still present and busy still. Moving to asynchronousrequest_queueremoval could have broken many scripts which relied on the removal to have been completed if there was no error. Document this expectation as well so that this doesn't regress userspace again.Quoted 3 lines
Using asynchronous
request_queueremoval however has helped us find other bugs. In the future we can test what could break with this arrangement by enablingCONFIG_DEBUG_KOBJECT_RELEASE.
Quoted 6 lines
You are adding documenation and
might_sleep()calls all over the place in here, making the "real" change in the patch hard to pick out.How about you split this up into 3 patches, one for documentation, one for
might_sleep()and one for the real change? Or maybe just 2 patches, but what you have here seems excessive.
Sure.
Luis
PATCH v4 2/5 block: move main block debugfs initialization to its own file
In reply to Luis Chamberlainmake_request-based drivers and and request-based drivers share some debugfs code. By moving this into its own file it makes it easier to expand and audit this shared code.
This patch contains no functional changes.
--- block/Makefile | 1 + block/blk-core.c | 9 +-------- block/blk-debugfs.c | 15 +++++++++++++++ block/blk.h | 7 +++++++ 4 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 block/blk-debugfs.c
block/Makefile +1 -0
@@ -10,6 +10,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-sysfs.o \ blk-mq-sysfs.o blk-mq-cpumap.o blk-mq-sched.o ioctl.o \ genhd.o ioprio.o badblocks.o partitions/ blk-rq-qos.o +obj-$(CONFIG_DEBUG_FS) += blk-debugfs.o obj-$(CONFIG_BOUNCE) += bounce.o obj-$(CONFIG_BLK_SCSI_REQUEST) += scsi_ioctl.o obj-$(CONFIG_BLK_DEV_BSG) += bsg.o
block/blk-core.c +1 -8
@@ -49,10 +49,6 @@#include "blk-pm.h" #include "blk-rq-qos.h" -#ifdef CONFIG_DEBUG_FS -struct dentry *blk_debugfs_root; -#endif -EXPORT_TRACEPOINT_SYMBOL_GPL (block_bio_remap);EXPORT_TRACEPOINT_SYMBOL_GPL (block_rq_remap);EXPORT_TRACEPOINT_SYMBOL_GPL (block_bio_complete);
@@ -1813,10 +1809,7 @@ int __init blk_dev_init(void) blk_requestq_cachep =kmem_cache_create ("request_queue" ,sizeof (struct request_queue ),0 ,SLAB_PANIC ,NULL ); - -#ifdef CONFIG_DEBUG_FS - blk_debugfs_root =debugfs_create_dir ("block" ,NULL ); -#endif +blk_debugfs_register ();return 0 ; }
block/blk-debugfs.c new file +15 -0
@@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* +* Shared request-based / make_request-based functionality +*/ +#include <linux/kernel.h> +#include <linux/blkdev.h> +#include <linux/debugfs.h> + +struct dentry *blk_debugfs_root; + +void blk_debugfs_register (void ) +{ + blk_debugfs_root =debugfs_create_dir ("block" ,NULL ); +}
block/blk.h +7 -0
@@ -456,5 +456,12 @@ struct request_queue *__blk_alloc_queue(int node_id);int __bio_add_pc_page (struct request_queue *q,struct bio *bio,struct page *page,unsigned int len,unsigned int offset,bool *same_page); +#ifdef CONFIG_DEBUG_FS +void blk_debugfs_register (void ); +#else +static inline void blk_debugfs_register (void ) +{ +} +#endif /* CONFIG_DEBUG_FS */ #endif /* BLK_INTERNAL_H */
Signature · 1 line
2.25.1
PATCH v4 3/5 blktrace: fix debugfs use after free
In reply to Luis ChamberlainOn commit 6ac93117ab00 ("blktrace: use existing disk debugfs directory") merged on v4.12 Omar fixed the original blktrace code for request-based drivers (multiqueue). This however left in place a possible crash, if you happen to abuse blktrace while racing to remove / add a device.
We used to use asynchronous removal of the request_queue, and with that the issue was easier to reproduce. Now that we have reverted to synchronous removal of the request_queue, the issue is still possible to reproduce, its however just a bit more difficult.
We essentially run two instances of break-blktrace which add/remove a loop device, and setup a blktrace and just never tear the blktrace down. We do this twice in parallel. This is easily reproduced with the break-blktrace run_0004.sh script.
We can end up with two types of panics each reflecting where we race, one a failed blktrace setup:
[ 252.426751] debugfs: Directory 'loop0' with parent 'block' already present!
[ 252.432265] BUG: kernel NULL pointer dereference, address: 00000000000000a0
[ 252.436592] #PF: supervisor write access in kernel mode
[ 252.439822] #PF: error_code(0x0002) - not-present page
[ 252.442967] PGD 0 P4D 0
[ 252.444656] Oops: 0002 [#1] SMP NOPTI
[ 252.446972] CPU: 10 PID: 1153 Comm: break-blktrace Tainted: G E 5.7.0-rc2-next-20200420+ #164
[ 252.452673] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014
[ 252.456343] RIP: 0010:down_write+0x15/0x40
[ 252.458146] Code: eb ca e8 ae 22 8d ff cc cc cc cc cc cc cc cc cc cc cc cc
cc cc 0f 1f 44 00 00 55 48 89 fd e8 52 db ff ff 31 c0 ba 01 00
00 00 <f0> 48 0f b1 55 00 75 0f 48 8b 04 25 c0 8b 01 00 48 89
45 08 5d
[ 252.463638] RSP: 0018:ffffa626415abcc8 EFLAGS: 00010246
[ 252.464950] RAX: 0000000000000000 RBX: ffff958c25f0f5c0 RCX: ffffff8100000000
[ 252.466727] RDX: 0000000000000001 RSI: ffffff8100000000 RDI: 00000000000000a0
[ 252.468482] RBP: 00000000000000a0 R08: 0000000000000000 R09: 0000000000000001
[ 252.470014] R10: 0000000000000000 R11: ffff958d1f9227ff R12: 0000000000000000
[ 252.471473] R13: ffff958c25ea5380 R14: ffffffff8cce15f1 R15: 00000000000000a0
[ 252.473346] FS: 00007f2e69dee540(0000) GS:ffff958c2fc80000(0000) knlGS:0000000000000000
[ 252.475225] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 252.476267] CR2: 00000000000000a0 CR3: 0000000427d10004 CR4: 0000000000360ee0
[ 252.477526] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 252.478776] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 252.479866] Call Trace:
[ 252.480322] simple_recursive_removal+0x4e/0x2e0
[ 252.481078] ? debugfs_remove+0x60/0x60
[ 252.481725] ? relay_destroy_buf+0x77/0xb0
[ 252.482662] debugfs_remove+0x40/0x60
[ 252.483518] blk_remove_buf_file_callback+0x5/0x10
[ 252.484328] relay_close_buf+0x2e/0x60
[ 252.484930] relay_open+0x1ce/0x2c0
[ 252.485520] do_blk_trace_setup+0x14f/0x2b0
[ 252.486187] __blk_trace_setup+0x54/0xb0
[ 252.486803] blk_trace_ioctl+0x90/0x140
[ 252.487423] ? do_sys_openat2+0x1ab/0x2d0
[ 252.488053] blkdev_ioctl+0x4d/0x260
[ 252.488636] block_ioctl+0x39/0x40
[ 252.489139] ksys_ioctl+0x87/0xc0
[ 252.489675] __x64_sys_ioctl+0x16/0x20
[ 252.490380] do_syscall_64+0x52/0x180
[ 252.491032] entry_SYSCALL_64_after_hwframe+0x44/0xa9And the other on the device removal:
[ 128.528940] debugfs: Directory 'loop0' with parent 'block' already present!
[ 128.615325] BUG: kernel NULL pointer dereference, address: 00000000000000a0
[ 128.619537] #PF: supervisor write access in kernel mode
[ 128.622700] #PF: error_code(0x0002) - not-present page
[ 128.625842] PGD 0 P4D 0
[ 128.627585] Oops: 0002 [#1] SMP NOPTI
[ 128.629871] CPU: 12 PID: 544 Comm: break-blktrace Tainted: G E 5.7.0-rc2-next-20200420+ #164
[ 128.635595] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014
[ 128.640471] RIP: 0010:down_write+0x15/0x40
[ 128.643041] Code: eb ca e8 ae 22 8d ff cc cc cc cc cc cc cc cc cc cc cc cc
cc cc 0f 1f 44 00 00 55 48 89 fd e8 52 db ff ff 31 c0 ba 01 00
00 00 <f0> 48 0f b1 55 00 75 0f 65 48 8b 04 25 c0 8b 01 00 48 89
45 08 5d
[ 128.650180] RSP: 0018:ffffa9c3c05ebd78 EFLAGS: 00010246
[ 128.651820] RAX: 0000000000000000 RBX: ffff8ae9a6370240 RCX: ffffff8100000000
[ 128.653942] RDX: 0000000000000001 RSI: ffffff8100000000 RDI: 00000000000000a0
[ 128.655720] RBP: 00000000000000a0 R08: 0000000000000002 R09: ffff8ae9afd2d3d0
[ 128.657400] R10: 0000000000000056 R11: 0000000000000000 R12: 0000000000000000
[ 128.659099] R13: 0000000000000000 R14: 0000000000000003 R15: 00000000000000a0
[ 128.660500] FS: 00007febfd995540(0000) GS:ffff8ae9afd00000(0000) knlGS:0000000000000000
[ 128.662204] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 128.663426] CR2: 00000000000000a0 CR3: 0000000420042003 CR4: 0000000000360ee0
[ 128.664776] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 128.666022] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 128.667282] Call Trace:
[ 128.667801] simple_recursive_removal+0x4e/0x2e0
[ 128.668663] ? debugfs_remove+0x60/0x60
[ 128.669368] debugfs_remove+0x40/0x60
[ 128.669985] blk_trace_free+0xd/0x50
[ 128.670593] __blk_trace_remove+0x27/0x40
[ 128.671274] blk_trace_shutdown+0x30/0x40
[ 128.671935] blk_release_queue+0x95/0xf0
[ 128.672589] kobject_put+0xa5/0x1b0
[ 128.673188] disk_release+0xa2/0xc0
[ 128.673786] device_release+0x28/0x80
[ 128.674376] kobject_put+0xa5/0x1b0
[ 128.674915] loop_remove+0x39/0x50 [loop]
[ 128.675511] loop_control_ioctl+0x113/0x130 [loop]
[ 128.676199] ksys_ioctl+0x87/0xc0
[ 128.676708] __x64_sys_ioctl+0x16/0x20
[ 128.677274] do_syscall_64+0x52/0x180
[ 128.677823] entry_SYSCALL_64_after_hwframe+0x44/0xa9The common theme here is:
debugfs: Directory 'loop0' with parent 'block' already present
This crash happens because of how blktrace uses the debugfs directory where it places its files. Upon init we always create the same directory which would be needed by blktrace but we only do this for make_request drivers (multiqueue) block drivers, but never for request-based block drivers. Furthermore, that directory is only created on init for the entire disk. This means that if you use blktrace on a partition, we'll always be creating a new directory regardless of whether or not you are doing blktrace on a make_request driver (multiqueue) or a request-based block drivers.
These directory creations are only associated with a path, and so when a debugfs_remove() is called it removes everything in its way. A device removal will remove all blktrace files, and so if a blktrace is still present a cleanup of blktrace files later will end up trying to remove dentries pointing to NULL.
We can fix the UAF by using a debugfs directory which moving forward will always be accessible if debugfs is enabled for both make_request drivers (multiqueue) and request-based block drivers, *and* for all partitions upon creation. This ensures that removal of the directories only happens on device removal and removes the race of the files underneath an active blktrace.
For partitions we simply symlink to the whole disk's debugfs_dir, as the debugfs_dir is shared anyway and this limits us to only run one blktrace for the entire disk.
We special-case a solution for scsi-generic which got blktrace support added by Christof via commit 6da127ad0918 ("blktrace: Add blktrace ioctls to SCSI generic devices") so upstream since v2.6.25. scsi-generic drives use a character device, however behind the scenes we have a scsi device with a request_queue. How this is used varies by class of driver (TYPE_DISK, TYPE_TYPE, etc). Care has to be taken into consideration of the fact that scsi drivers will probe asynchronously but the scsi-generic class_interface sg_add_device() will complete before. This means sd_probe() will use device_add_disk() for TYPE_DISK and have its debugfs_dir created *after* the scsi-generic device is created.
For scsi-generic then we symlink to the real debugfs_dir only during a blktrace ioctl, but we do this only once. We also have to special-case yet another solution for drivers which use the bsg queue.
This goes tested with:
o nvme partitions
o ISCSI with tgt, and blktracing against scsi-generic with:
o block
o tape
o cdrom
o media changer
Screenshots of what the debugfs for block looks like after running blktrace on a system with sg0 which has a raid controllerand then sg1 as the media changer:
# ls -l /sys/kernel/debug/block total 0 drwxr-xr-x 3 root root 0 May 9 02:31 bsg drwxr-xr-x 19 root root 0 May 9 02:31 nvme0n1 drwxr-xr-x 19 root root 0 May 9 02:31 nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p1 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p2 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p3 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p5 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p6 -> nvme1n1 drwxr-xr-x 2 root root 0 May 9 02:33 sch0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg0 -> bsg/2:0:0:0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg1 -> sch0 drwxr-xr-x 5 root root 0 May 9 02:31 vda lrwxrwxrwx 1 root root 0 May 9 02:31 vda1 -> vda
Code for handling the ebugfs_dir did get more complicatd for scsi-generic but this is technical debt. For the other types of devices, this simplifies the code considerably, with the only penalty now being that we're always creating the request queue debugfs directory for the request-based block device drivers.
The symlink use also makes it clearer when the request_queue is shared.
This patch is part of the work which disputes the severity of CVE-2019-19770 which shows this issue is not a core debugfs issue, but a misuse of debugfs within blktace.
--- block/blk-debugfs.c | 187 +++++++++++++++++++++++++++++++++++ block/blk-mq-debugfs.c | 5 - block/blk-sysfs.c | 3 + block/blk.h | 16 +++ block/bsg.c | 2 + block/partitions/core.c | 9 ++ drivers/scsi/ch.c | 1 + drivers/scsi/sg.c | 75 ++++++++++++++ drivers/scsi/st.c | 2 + include/linux/blkdev.h | 4 +- include/linux/blktrace_api.h | 1 - include/linux/genhd.h | 69 +++++++++++++ kernel/trace/blktrace.c | 24 +++-- 13 files changed, 385 insertions(+), 13 deletions(-)
block/blk-debugfs.c +187 -0
@@ -8,8 +8,195 @@#include <linux/debugfs.h> struct dentry *blk_debugfs_root; +struct dentry *blk_debugfs_bsg =NULL ; + +/** +* enum blk_debugfs_dir_type - block device debugfs directory type +* @BLK_DBG_DIR_BASE: the block device debugfs_dir exists on the base +* system <system-debugfs-dir>/block/ debugfs directory. +* @BLK_DBG_DIR_BSG: the block device debugfs_dir is under the directory +* <system-debugfs-dir>/block/bsg/ +*/ +enum blk_debugfs_dir_type { +BLK_DBG_DIR_BASE =1 , +BLK_DBG_DIR_BSG , +};void blk_debugfs_register (void ) { blk_debugfs_root =debugfs_create_dir ("block" ,NULL ); } + +static struct dentry *queue_get_base_dir (enum blk_debugfs_dir_type type) +{ +switch (type) { +case BLK_DBG_DIR_BASE : +return blk_debugfs_root; +case BLK_DBG_DIR_BSG : +return blk_debugfs_bsg; + } +return NULL ; +} + +static void queue_debugfs_register_type (struct request_queue *q, +const char *name, +enum blk_debugfs_dir_type type) +{ +struct dentry *base_dir =queue_get_base_dir (type); + + q->debugfs_dir =debugfs_create_dir (name, base_dir); +} + +/** +* blk_queue_debugfs_register - register the debugfs_dir for the block device +* @q: the associated request_queue of the block device +* @name: the name of the block device exposed +* +* This is used to create the debugfs_dir used by the block layer and blktrace. +* Drivers which use any of the *add_disk*() calls or variants have this called +* automatically for them. This directory is removed automatically on +* blk_release_queue() once the request_queue reference count reaches 0. +*/ +void blk_queue_debugfs_register (struct request_queue *q,const char *name) +{ +queue_debugfs_register_type (q, name,BLK_DBG_DIR_BASE ); +} +EXPORT_SYMBOL_GPL (blk_queue_debugfs_register); + +/** +* blk_queue_debugfs_unregister - remove the debugfs_dir for the block device +* @q: the associated request_queue of the block device +* +* Removes the debugfs_dir for the request_queue on the associated block device. +* This is handled for you on blk_release_queue(), and that should only be +* called once. +* +* Since we don't care where the debugfs_dir was created this is used for all +* types of of enum blk_debugfs_dir_type. +*/ +void blk_queue_debugfs_unregister (struct request_queue *q) +{ +debugfs_remove_recursive (q->debugfs_dir); +} + +static struct dentry *queue_debugfs_symlink_type (struct request_queue *q, +const char *src, +const char *dst, +enum blk_debugfs_dir_type type) +{ +struct dentry *dentry =ERR_PTR (-EINVAL ); +char *dir_dst; + + dir_dst =kzalloc (PATH_MAX ,GFP_KERNEL ); +if (!dir_dst) +return dentry; + +switch (type) { +case BLK_DBG_DIR_BASE : +if (dst) +snprintf (dir_dst,PATH_MAX ,"%s" , dst); +else if (!IS_ERR_OR_NULL (q->debugfs_dir)) +snprintf (dir_dst,PATH_MAX ,"%s" , + q->debugfs_dir->d_name.name); +else + goto out; +break ; +case BLK_DBG_DIR_BSG : +if (dst) +snprintf (dir_dst,PATH_MAX ,"bsg/%s" , dst); +else + goto out; +break ; + } + +/* +* The base block debugfs directory is always used for the symlinks, +* their target is what changes. +*/ + dentry =debugfs_create_symlink (src, blk_debugfs_root, dir_dst); +out: +kfree (dir_dst); + +return dentry; +} + +/** +* blk_queue_debugfs_symlink - symlink to the real block device debugfs_dir +* @q: the request queue where we know the debugfs_dir exists or will exist +* eventually. Cannot be NULL. +* @src: name of the exposed device we wish to associate to the block device +* @dst: the name of the directory to which we want to symlink to, may be NULL +* if you do not know what this may be, but only if your base block device +* is not bsg. If you set this to NULL, we will have no other option but +* to look at the request_queue to infer the name, but you must ensure +* it is already be set, be mindful of asynchronous probes. +* +* Some devices don't have a request_queue of their own, however, they have an +* association to one and have historically supported using the same +* debugfs_dir which has been used to represent the whole disk for blktrace +* functionality. Such is the case for partitions and for scsi-generic devices. +* They share the same request_queue and debugfs_dir as with the whole disk for +* blktrace purposes. This helper allows such association to be made explicit +* and enable blktrace functionality for them. scsi-generic devices representing +* scsi device such as block, cdrom, tape, media changer register their own +* debug_dir already and share the same request_queue as with scsi-generic, as +* such the respective scsi-generic debugfs_dir is just a symlink to these +* driver's debugfs_dir. +* +* To remove use debugfs_remove() on the symlink dentry returned by this +* function. The block layer will not clean this up for you, you must remove +* it yourself in case of device removal. +*/ +struct dentry *blk_queue_debugfs_symlink (struct request_queue *q, +const char *src, +const char *dst) +{ +return queue_debugfs_symlink_type (q, src, dst,BLK_DBG_DIR_BASE ); +} +EXPORT_SYMBOL_GPL (blk_queue_debugfs_symlink); + +#ifdef CONFIG_BLK_DEV_BSG + +void blk_debugfs_register_bsg (void ) +{ + blk_debugfs_bsg =debugfs_create_dir ("bsg" , blk_debugfs_root); +} + +/** +* blk_queue_debugfs_register_bsg - create the debugfs_dir for bsg block devices +* @q: the associated request_queue of the block device +* @name: the name of the block device exposed +* +* This is used to create the debugfs_dir used by the Block layer SCSI generic +* (bsg) driver. This is to be used only by the scsi-generic driver on behalf +* of scsi devices which work as scsi controllers or transports. +* +* This directory is cleaned up for all drivers automatically on +* blk_release_queue() once the request_queue reference count reaches 0. +*/ +void blk_queue_debugfs_register_bsg (struct request_queue *q,const char *name) +{ +queue_debugfs_register_type (q, name,BLK_DBG_DIR_BSG ); +} +EXPORT_SYMBOL_GPL (blk_queue_debugfs_register_bsg); + +/** +* blk_queue_debugfs_symlink_bsg - symlink to the bsg debugfs_dir +* @q: the request queue where we know the debugfs_dir exists or will exist +* eventually. Cannot be NULL. +* @src: name of the scsi-generic device we wish to associate to the bsg +* request_queue. +* @dst: the name of the bsg request_queue debugfs_dir to which we want to +* symlink to. This cannot be NULL. +* +* This is used by scsi-generic devices representing raid controllers / +* transport drivers. +*/ +struct dentry *blk_queue_debugfs_bsg_symlink (struct request_queue *q, +const char *src, +const char *dst) +{ +return queue_debugfs_symlink_type (q, src, dst,BLK_DBG_DIR_BSG ); +} +EXPORT_SYMBOL_GPL (blk_queue_debugfs_bsg_symlink); +#endif /* CONFIG_BLK_DEV_BSG */
block/blk-mq-debugfs.c +0 -5
@@ -822,9 +822,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);/*
@@ -855,9 +852,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,
block/blk-sysfs.c +3 -0
@@ -905,6 +905,7 @@ static void blk_release_queue(struct kobject *kobj)blk_trace_shutdown (q); +blk_queue_debugfs_unregister (q);if (queue_is_mq (q))blk_mq_debugfs_unregister (q);
@@ -976,6 +977,8 @@ int blk_register_queue(struct gendisk *disk) goto unlock; } +blk_queue_debugfs_register (q,kobject_name (q->kobj.parent)); +if (queue_is_mq (q)) {__blk_mq_register_dev (dev, q);blk_mq_debugfs_register (q);
block/blk.h +16 -0
@@ -458,10 +458,26 @@ int __bio_add_pc_page(struct request_queue *q, struct bio *bio, bool *same_page);#ifdef CONFIG_DEBUG_FS void blk_debugfs_register (void ); +void blk_queue_debugfs_unregister (struct request_queue *q); +void blk_part_debugfs_register (struct hd_struct *p,const char *name); +void blk_part_debugfs_unregister (struct hd_struct *p);#else static inline void blk_debugfs_register (void ) { } + +static inline void blk_queue_debugfs_unregister (struct request_queue *q) +{ +} + +static inline void blk_part_debugfs_register (struct hd_struct *p, +const char *name) +{ +} + +static inline void blk_part_debugfs_unregister (struct hd_struct *p) +{ +}#endif /* CONFIG_DEBUG_FS */ #endif /* BLK_INTERNAL_H */
block/bsg.c +2 -0
@@ -503,6 +503,8 @@ static int __init bsg_init(void)if (ret) goto unregister_chrdev; +blk_debugfs_register_bsg (); + printk(KERN_INFO BSG_DESCRIPTION " version " BSG_VERSION " loaded (major %d)\n", bsg_major);return 0 ;
block/partitions/core.c +9 -0
@@ -10,6 +10,7 @@#include <linux/vmalloc.h> #include <linux/blktrace_api.h> #include <linux/raid/detect.h> +#include <linux/debugfs.h> #include "check.h" static int (*check_part[])(struct parsed_partitions *) = {
@@ -309,6 +310,9 @@ void delete_partition(struct gendisk *disk, struct hd_struct *part)struct disk_part_tbl *ptbl =rcu_dereference_protected (disk->part_tbl,1 ); +#ifdef CONFIG_DEBUG_FS +debugfs_remove (part->debugfs_sym); +#endif rcu_assign_pointer (ptbl->part[part->partno],NULL );rcu_assign_pointer (ptbl->last_lookup,NULL );kobject_put (part->holder_dir);
@@ -450,6 +454,11 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,/* everything is up and running, commence */ rcu_assign_pointer (ptbl->part[partno], p); +#ifdef CONFIG_DEBUG_FS + p->debugfs_sym =blk_queue_debugfs_symlink (disk->queue,dev_name (pdev), + disk->disk_name); +#endif +/* suppress uevent if the disk suppresses it */ if (!dev_get_uevent_suppress (ddev))kobject_uevent (&pdev->kobj,KOBJ_ADD );
drivers/scsi/ch.c +1 -0
@@ -971,6 +971,7 @@ static int ch_probe(struct device *dev)mutex_unlock (&ch->lock);dev_set_drvdata (dev, ch); +blk_queue_debugfs_register (sd->request_queue,dev_name (class_dev));sdev_printk (KERN_INFO , sd,"Attached scsi changer %s\n" , ch->name);return 0 ;
drivers/scsi/sg.c +75 -0
105 lines not on this page · raw message
drivers/scsi/st.c +2 -0
@@ -4417,6 +4417,8 @@ static int st_probe(struct device *dev)if (error) goto out_remove_devs;scsi_autopm_put_device (SDp); +blk_queue_debugfs_register (tpnt->device->request_queue, +tape_name (tpnt));sdev_printk (KERN_NOTICE , SDp,"Attached scsi tape %s\n" ,tape_name (tpnt));
include/linux/blkdev.h +3 -1
@@ -561,8 +561,10 @@ struct request_queue {struct list_head tag_set_list;struct bio_set bio_split; -#ifdef CONFIG_BLK_DEBUG_FS +#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dir; +#endif +#ifdef CONFIG_BLK_DEBUG_FS struct dentry *sched_debugfs_dir;struct dentry *rqos_debugfs_dir;#endif
include/linux/blktrace_api.h +0 -1
@@ -22,7 +22,6 @@ struct blk_trace {u64 end_lba;u32 pid;u32 dev; -struct dentry *dir;struct dentry *dropped_file;struct dentry *msg_file;struct list_head running_list;
include/linux/genhd.h +69 -0
79 lines not on this page · raw message
kernel/trace/blktrace.c +18 -6
@@ -311,7 +311,6 @@ static void blk_trace_free(struct blk_trace *bt)debugfs_remove (bt->msg_file);debugfs_remove (bt->dropped_file);relay_close (bt->rchan); -debugfs_remove (bt->dir);free_percpu (bt->sequence);free_percpu (bt->msg_data);kfree (bt);
@@ -509,9 +508,24 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, ret = -ENOENT ; - dir =debugfs_lookup (buts->name, blk_debugfs_root); -if (!dir) - bt->dir = dir =debugfs_create_dir (buts->name, blk_debugfs_root); + dir = q->debugfs_dir; + +/* +* Although the directory here is from debugfs, and we typically do not +* care about NULL dirs as debugfs is typically only used for debugging, +* we rely on the directory to exist to place files which we then use +* for blktrace userspace functionality. Without this directory +* blktrace would not work. Enabling blktrace functionality enables +* debugfs too, as such, we *really* do want to check for this and must +* ensure it was set before chugging on. If NULL were used below, we'd +* also end up creating the debugfs files under the block root +* directory, which we definitely do not want. +*/ +if (IS_ERR_OR_NULL (dir)) { +pr_warn ("debugfs_dir not present for %s so skipping\n" , + buts->name); + goto err; + } bt->dev = dev;atomic_set (&bt->dropped,0 );
@@ -551,8 +565,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;
Signature · 1 line
2.25.1
kbuild test robot
build botRe: [PATCH v4 3/5] blktrace: fix debugfs use after free
I love your patch! Yet something to improve:
Hi Luis,
I love your patch! Yet something to improve:
[auto build test ERROR on block/for-next]
[also build test ERROR on next-20200508]
[cannot apply to mkp-scsi/for-next scsi/for-next linus/master v5.7-rc4] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Luis-Chamberlain/block-fix-blktrace-debugfs-use-after-free/20200509-140139 base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next config: um-allnoconfig (attached as .config) compiler: gcc-7 (Ubuntu 7.5.0-6ubuntu2) 7.5.0 reproduce: # save the attached .config to linux build tree make ARCH=um
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>):
ld: drivers/char/random.o: in function `blk_queue_debugfs_symlink':
>> random.c:(.text+0x10f0): multiple definition of `blk_queue_debugfs_symlink'; init/do_mounts.o:do_mounts.c:(.text+0x10): first defined here
ld: drivers/base/core.o: in function `blk_queue_debugfs_symlink':
core.c:(.text+0x800): multiple definition of `blk_queue_debugfs_symlink'; init/do_mounts.o:do_mounts.c:(.text+0x10): first defined here
ld: drivers/base/class.o: in function `blk_queue_debugfs_symlink':
class.c:(.text+0xd0): multiple definition of `blk_queue_debugfs_symlink'; init/do_mounts.o:do_mounts.c:(.text+0x10): first defined here
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kbuild test robot
build botRe: [PATCH v4 3/5] blktrace: fix debugfs use after free
In reply to Luis ChamberlainI love your patch! Yet something to improve:
Hi Luis,
I love your patch! Yet something to improve:
[auto build test ERROR on block/for-next]
[also build test ERROR on next-20200508]
[cannot apply to mkp-scsi/for-next scsi/for-next linus/master v5.7-rc4] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Luis-Chamberlain/block-fix-blktrace-debugfs-use-after-free/20200509-140139 base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next config: parisc-allnoconfig (attached as .config) compiler: hppa-linux-gcc (GCC) 9.3.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day GCC_VERSION=9.3.0 make.cross ARCH=parisc
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>):
hppa-linux-ld: drivers/char/random.o: in function `blk_queue_debugfs_symlink':
>> (.text+0x1130): multiple definition of `blk_queue_debugfs_symlink'; init/do_mounts.o:(.text+0x1c): first defined here
hppa-linux-ld: drivers/base/core.o: in function `blk_queue_debugfs_symlink':
(.text+0xc18): multiple definition of `blk_queue_debugfs_symlink'; init/do_mounts.o:(.text+0x1c): first defined here
hppa-linux-ld: drivers/base/class.o: in function `blk_queue_debugfs_symlink':
(.text+0xf8): multiple definition of `blk_queue_debugfs_symlink'; init/do_mounts.o:(.text+0x1c): first defined here
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
Re: [PATCH v4 3/5] blktrace: fix debugfs use after free
In reply to Luis ChamberlainQuoted 4 lines · Luis Chamberlain
Screenshots of what the debugfs for block looks like after running blktrace on a system with sg0 which has a raid controllerand then sg1 as the media changer:
Quoted 15 lines
# ls -l /sys/kernel/debug/block total 0 drwxr-xr-x 3 root root 0 May 9 02:31 bsg drwxr-xr-x 19 root root 0 May 9 02:31 nvme0n1 drwxr-xr-x 19 root root 0 May 9 02:31 nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p1 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p2 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p3 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p5 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p6 -> nvme1n1 drwxr-xr-x 2 root root 0 May 9 02:33 sch0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg0 -> bsg/2:0:0:0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg1 -> sch0 drwxr-xr-x 5 root root 0 May 9 02:31 vda lrwxrwxrwx 1 root root 0 May 9 02:31 vda1 -> vda
So this patch creates one soft link per partition at partition creation time instead of letting the blktrace code create one directory per partition when tracing starts? Does this break running blktrace simultaneously for a partition and for the entire block device?
Quoted 30 lines
block/blk-debugfs.c +30
-0+static struct dentry *queue_debugfs_symlink_type (struct request_queue *q, +const char *src, +const char *dst, +enum blk_debugfs_dir_type type) +{ +struct dentry *dentry =ERR_PTR (-EINVAL ); +char *dir_dst; + + dir_dst =kzalloc (PATH_MAX ,GFP_KERNEL ); +if (!dir_dst) +return dentry; + +switch (type) { +case BLK_DBG_DIR_BASE : +if (dst) +snprintf (dir_dst,PATH_MAX ,"%s" , dst); +else if (!IS_ERR_OR_NULL (q->debugfs_dir)) +snprintf (dir_dst,PATH_MAX ,"%s" , + q->debugfs_dir->d_name.name); +else + goto out; +break ; +case BLK_DBG_DIR_BSG : +if (dst) +snprintf (dir_dst,PATH_MAX ,"bsg/%s" , dst); +else + goto out; +break ; + } +
block/blk-debugfs.c line 121
Quoted 10 lines
block/blk-debugfs.c +10
-0+/* +* The base block debugfs directory is always used for the symlinks, +* their target is what changes. +*/ + dentry =debugfs_create_symlink (src, blk_debugfs_root, dir_dst); +out: +kfree (dir_dst); + +return dentry; +}
Please use kasprintf() instead of k?alloc() followed by snprintf().
Thanks,
Bart.
Re: [PATCH v4 3/5] blktrace: fix debugfs use after free
Quoted 21 lines · Bart Van Assche
Quoted 4 lines · Luis Chamberlain
Screenshots of what the debugfs for block looks like after running blktrace on a system with sg0 which has a raid controllerand then sg1 as the media changer:
Quoted 15 lines
# ls -l /sys/kernel/debug/block total 0 drwxr-xr-x 3 root root 0 May 9 02:31 bsg drwxr-xr-x 19 root root 0 May 9 02:31 nvme0n1 drwxr-xr-x 19 root root 0 May 9 02:31 nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p1 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p2 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p3 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p5 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p6 -> nvme1n1 drwxr-xr-x 2 root root 0 May 9 02:33 sch0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg0 -> bsg/2:0:0:0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg1 -> sch0 drwxr-xr-x 5 root root 0 May 9 02:31 vda lrwxrwxrwx 1 root root 0 May 9 02:31 vda1 -> vda
Quoted 3 lines
So this patch creates one soft link per partition at partition creation time instead of letting the blktrace code create one directory per partition when tracing starts?
Yes.
Quoted 2 lines
Does this break running blktrace
simultaneously for a partition and for the entire block device?
blktrace already has this limitation, one blktrace is only allowed per request_queue, the next patch clarifies this, as we currently just error out without telling the user what has happened.e user what has happened.e user what has happened.e user what has happened.
Quoted 34 lines
Quoted 34 lines
block/blk-debugfs.c +34
-0+static struct dentry *queue_debugfs_symlink_type (struct request_queue *q, +const char *src, +const char *dst, +enum blk_debugfs_dir_type type) +{ +struct dentry *dentry =ERR_PTR (-EINVAL ); +char *dir_dst; + + dir_dst =kzalloc (PATH_MAX ,GFP_KERNEL ); +if (!dir_dst) +return dentry; + +switch (type) { +case BLK_DBG_DIR_BASE : +if (dst) +snprintf (dir_dst,PATH_MAX ,"%s" , dst); +else if (!IS_ERR_OR_NULL (q->debugfs_dir)) +snprintf (dir_dst,PATH_MAX ,"%s" , + q->debugfs_dir->d_name.name); +else + goto out; +break ; +case BLK_DBG_DIR_BSG : +if (dst) +snprintf (dir_dst,PATH_MAX ,"bsg/%s" , dst); +else + goto out; +break ; + } + +/* +* The base block debugfs directory is always used for the symlinks, +* their target is what changes. +*/
Quoted 8 lines
block/blk-debugfs.c line 121
Quoted 6 lines
block/blk-debugfs.c +6
-0+ dentry =debugfs_create_symlink (src, blk_debugfs_root, dir_dst); +out: +kfree (dir_dst); + +return dentry; +}Please use
kasprintf()instead of k?alloc() followed bysnprintf().
Sure thing.
Luis
Re: [PATCH v4 3/5] blktrace: fix debugfs use after free
In reply to Luis ChamberlainQuoted 308 lines · Luis Chamberlain
On commit
6ac93117ab00("blktrace: use existing disk debugfs directory") merged on v4.12 Omar fixed the original blktrace code for request-based drivers (multiqueue). This however left in place a possible crash, if you happen to abuse blktrace while racing to remove / add a device.We used to use asynchronous removal of the
request_queue, and with that the issue was easier to reproduce. Now that we have reverted to synchronous removal of therequest_queue, the issue is still possible to reproduce, its however just a bit more difficult.We essentially run two instances of break-blktrace which add/remove a loop device, and setup a blktrace and just never tear the blktrace down. We do this twice in parallel. This is easily reproduced with the break-blktrace
run_0004.shscript.We can end up with two types of panics each reflecting where we race, one a failed blktrace setup:
[ 252.426751] debugfs: Directory 'loop0' with parent 'block' already present! [ 252.432265] BUG: kernel NULL pointer dereference, address: 00000000000000a0 [ 252.436592] #PF: supervisor write access in kernel mode [ 252.439822] #PF: error_code(0x0002) - not-present page [ 252.442967] PGD 0 P4D 0 [ 252.444656] Oops: 0002 [#1] SMP NOPTI [ 252.446972] CPU: 10 PID: 1153 Comm: break-blktrace Tainted: G E 5.7.0-rc2-next-20200420+ #164 [ 252.452673] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014 [ 252.456343] RIP: 0010:down_write+0x15/0x40 [ 252.458146] Code: eb ca e8 ae 22 8d ff cc cc cc cc cc cc cc cc cc cc cc cc cc cc 0f 1f 44 00 00 55 48 89 fd e8 52 db ff ff 31 c0 ba 01 00 00 00 <f0> 48 0f b1 55 00 75 0f 48 8b 04 25 c0 8b 01 00 48 89 45 08 5d [ 252.463638] RSP: 0018:ffffa626415abcc8 EFLAGS: 00010246 [ 252.464950] RAX: 0000000000000000 RBX: ffff958c25f0f5c0 RCX: ffffff8100000000 [ 252.466727] RDX: 0000000000000001 RSI: ffffff8100000000 RDI: 00000000000000a0 [ 252.468482] RBP: 00000000000000a0 R08: 0000000000000000 R09: 0000000000000001 [ 252.470014] R10: 0000000000000000 R11: ffff958d1f9227ff R12: 0000000000000000 [ 252.471473] R13: ffff958c25ea5380 R14: ffffffff8cce15f1 R15: 00000000000000a0 [ 252.473346] FS: 00007f2e69dee540(0000) GS:ffff958c2fc80000(0000) knlGS:0000000000000000 [ 252.475225] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 252.476267] CR2: 00000000000000a0 CR3: 0000000427d10004 CR4: 0000000000360ee0 [ 252.477526] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 252.478776] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 252.479866] Call Trace: [ 252.480322] simple_recursive_removal+0x4e/0x2e0 [ 252.481078] ? debugfs_remove+0x60/0x60 [ 252.481725] ? relay_destroy_buf+0x77/0xb0 [ 252.482662] debugfs_remove+0x40/0x60 [ 252.483518] blk_remove_buf_file_callback+0x5/0x10 [ 252.484328] relay_close_buf+0x2e/0x60 [ 252.484930] relay_open+0x1ce/0x2c0 [ 252.485520] do_blk_trace_setup+0x14f/0x2b0 [ 252.486187] __blk_trace_setup+0x54/0xb0 [ 252.486803] blk_trace_ioctl+0x90/0x140 [ 252.487423] ? do_sys_openat2+0x1ab/0x2d0 [ 252.488053] blkdev_ioctl+0x4d/0x260 [ 252.488636] block_ioctl+0x39/0x40 [ 252.489139] ksys_ioctl+0x87/0xc0 [ 252.489675] __x64_sys_ioctl+0x16/0x20 [ 252.490380] do_syscall_64+0x52/0x180 [ 252.491032] entry_SYSCALL_64_after_hwframe+0x44/0xa9And the other on the device removal:
[ 128.528940] debugfs: Directory 'loop0' with parent 'block' already present! [ 128.615325] BUG: kernel NULL pointer dereference, address: 00000000000000a0 [ 128.619537] #PF: supervisor write access in kernel mode [ 128.622700] #PF: error_code(0x0002) - not-present page [ 128.625842] PGD 0 P4D 0 [ 128.627585] Oops: 0002 [#1] SMP NOPTI [ 128.629871] CPU: 12 PID: 544 Comm: break-blktrace Tainted: G E 5.7.0-rc2-next-20200420+ #164 [ 128.635595] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014 [ 128.640471] RIP: 0010:down_write+0x15/0x40 [ 128.643041] Code: eb ca e8 ae 22 8d ff cc cc cc cc cc cc cc cc cc cc cc cc cc cc 0f 1f 44 00 00 55 48 89 fd e8 52 db ff ff 31 c0 ba 01 00 00 00 <f0> 48 0f b1 55 00 75 0f 65 48 8b 04 25 c0 8b 01 00 48 89 45 08 5d [ 128.650180] RSP: 0018:ffffa9c3c05ebd78 EFLAGS: 00010246 [ 128.651820] RAX: 0000000000000000 RBX: ffff8ae9a6370240 RCX: ffffff8100000000 [ 128.653942] RDX: 0000000000000001 RSI: ffffff8100000000 RDI: 00000000000000a0 [ 128.655720] RBP: 00000000000000a0 R08: 0000000000000002 R09: ffff8ae9afd2d3d0 [ 128.657400] R10: 0000000000000056 R11: 0000000000000000 R12: 0000000000000000 [ 128.659099] R13: 0000000000000000 R14: 0000000000000003 R15: 00000000000000a0 [ 128.660500] FS: 00007febfd995540(0000) GS:ffff8ae9afd00000(0000) knlGS:0000000000000000 [ 128.662204] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 128.663426] CR2: 00000000000000a0 CR3: 0000000420042003 CR4: 0000000000360ee0 [ 128.664776] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 128.666022] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 128.667282] Call Trace: [ 128.667801] simple_recursive_removal+0x4e/0x2e0 [ 128.668663] ? debugfs_remove+0x60/0x60 [ 128.669368] debugfs_remove+0x40/0x60 [ 128.669985] blk_trace_free+0xd/0x50 [ 128.670593] __blk_trace_remove+0x27/0x40 [ 128.671274] blk_trace_shutdown+0x30/0x40 [ 128.671935] blk_release_queue+0x95/0xf0 [ 128.672589] kobject_put+0xa5/0x1b0 [ 128.673188] disk_release+0xa2/0xc0 [ 128.673786] device_release+0x28/0x80 [ 128.674376] kobject_put+0xa5/0x1b0 [ 128.674915] loop_remove+0x39/0x50 [loop] [ 128.675511] loop_control_ioctl+0x113/0x130 [loop] [ 128.676199] ksys_ioctl+0x87/0xc0 [ 128.676708] __x64_sys_ioctl+0x16/0x20 [ 128.677274] do_syscall_64+0x52/0x180 [ 128.677823] entry_SYSCALL_64_after_hwframe+0x44/0xa9The common theme here is:
debugfs: Directory 'loop0' with parent 'block' already present
This crash happens because of how blktrace uses the debugfs directory where it places its files. Upon init we always create the same directory which would be needed by blktrace but we only do this for
make_requestdrivers (multiqueue) block drivers, but never for request-based block drivers. Furthermore, that directory is only created on init for the entire disk. This means that if you use blktrace on a partition, we'll always be creating a new directory regardless of whether or not you are doing blktrace on amake_requestdriver (multiqueue) or a request-based block drivers.These directory creations are only associated with a path, and so when a
debugfs_remove()is called it removes everything in its way. A device removal will remove all blktrace files, and so if a blktrace is still present a cleanup of blktrace files later will end up trying to remove dentries pointing to NULL.We can fix the UAF by using a debugfs directory which moving forward will always be accessible if debugfs is enabled for both
make_requestdrivers (multiqueue) and request-based block drivers, *and* for all partitions upon creation. This ensures that removal of the directories only happens on device removal and removes the race of the files underneath an active blktrace.For partitions we simply symlink to the whole disk's
debugfs_dir, as thedebugfs_diris shared anyway and this limits us to only run one blktrace for the entire disk.We special-case a solution for scsi-generic which got blktrace support added by Christof via commit
6da127ad0918("blktrace: Add blktrace ioctls to SCSI generic devices") so upstream since v2.6.25. scsi-generic drives use a character device, however behind the scenes we have a scsi device with arequest_queue. How this is used varies by class of driver (TYPE_DISK,TYPE_TYPE, etc). Care has to be taken into consideration of the fact that scsi drivers will probe asynchronously but the scsi-genericclass_interfacesg_add_device()will complete before. This meanssd_probe()will usedevice_add_disk()forTYPE_DISKand have itsdebugfs_dircreated *after* the scsi-generic device is created.For scsi-generic then we symlink to the real
debugfs_dironly during a blktrace ioctl, but we do this only once. We also have to special-case yet another solution for drivers which use the bsg queue.This goes tested with:
o nvme partitions
o ISCSI with tgt, and blktracing against scsi-generic with:
o block
o tape
o cdrom
o media changerScreenshots of what the debugfs for block looks like after running blktrace on a system with sg0 which has a raid controllerand then sg1 as the media changer:
# ls -l /sys/kernel/debug/block total 0 drwxr-xr-x 3 root root 0 May 9 02:31 bsg drwxr-xr-x 19 root root 0 May 9 02:31 nvme0n1 drwxr-xr-x 19 root root 0 May 9 02:31 nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p1 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p2 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p3 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p5 -> nvme1n1 lrwxrwxrwx 1 root root 0 May 9 02:31 nvme1n1p6 -> nvme1n1 drwxr-xr-x 2 root root 0 May 9 02:33 sch0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg0 -> bsg/2:0:0:0 lrwxrwxrwx 1 root root 0 May 9 02:33 sg1 -> sch0 drwxr-xr-x 5 root root 0 May 9 02:31 vda lrwxrwxrwx 1 root root 0 May 9 02:31 vda1 -> vdaCode for handling the
ebugfs_dirdid get more complicatd for scsi-generic but this is technical debt. For the other types of devices, this simplifies the code considerably, with the only penalty now being that we're always creating the request queue debugfs directory for the request-based block device drivers.The symlink use also makes it clearer when the
request_queueis shared.This patch is part of the work which disputes the severity of CVE-2019-19770 which shows this issue is not a core debugfs issue, but a misuse of debugfs within blktace.
Cc Bart Van Assche <[email protected]>Cc Omar Sandoval <[email protected]>Cc Hannes Reinecke <[email protected]>Cc Nicolai Stange <[email protected]>Cc Greg Kroah-Hartman <[email protected]>Cc Michal Hocko <[email protected]>Cc yu kuai <[email protected]>Cc Christof Schmitt <[email protected]>Reported-by [email protected]Fixes 6ac93117ab00 ("blktrace: use existing disk debugfs directory")Signed-off-by Luis Chamberlain <[email protected]>--- block/blk-debugfs.c | 187 +++++++++++++++++++++++++++++++++++ block/blk-mq-debugfs.c | 5 - block/blk-sysfs.c | 3 + block/blk.h | 16 +++ block/bsg.c | 2 + block/partitions/core.c | 9 ++ drivers/scsi/ch.c | 1 + drivers/scsi/sg.c | 75 ++++++++++++++ drivers/scsi/st.c | 2 + include/linux/blkdev.h | 4 +- include/linux/blktrace_api.h | 1 - include/linux/genhd.h | 69 +++++++++++++ kernel/trace/blktrace.c | 24 +++-- 13 files changed, 385 insertions(+), 13 deletions(-)block/blk-debugfs.c +75
-0@@ -8,8 +8,195 @@#include <linux/debugfs.h> struct dentry *blk_debugfs_root; +struct dentry *blk_debugfs_bsg =NULL ; + +/** +* enum blk_debugfs_dir_type - block device debugfs directory type +* @BLK_DBG_DIR_BASE: the block device debugfs_dir exists on the base +* system <system-debugfs-dir>/block/ debugfs directory. +* @BLK_DBG_DIR_BSG: the block device debugfs_dir is under the directory +* <system-debugfs-dir>/block/bsg/ +*/ +enum blk_debugfs_dir_type { +BLK_DBG_DIR_BASE =1 , +BLK_DBG_DIR_BSG , +};void blk_debugfs_register (void ) { blk_debugfs_root =debugfs_create_dir ("block" ,NULL ); } + +static struct dentry *queue_get_base_dir (enum blk_debugfs_dir_type type) +{ +switch (type) { +case BLK_DBG_DIR_BASE : +return blk_debugfs_root; +case BLK_DBG_DIR_BSG : +return blk_debugfs_bsg; + } +return NULL ; +} + +static void queue_debugfs_register_type (struct request_queue *q, +const char *name, +enum blk_debugfs_dir_type type) +{ +struct dentry *base_dir =queue_get_base_dir (type); + + q->debugfs_dir =debugfs_create_dir (name, base_dir); +} + +/** +* blk_queue_debugfs_register - register the debugfs_dir for the block device +* @q: the associated request_queue of the block device +* @name: the name of the block device exposed +* +* This is used to create the debugfs_dir used by the block layer and blktrace. +* Drivers which use any of the *add_disk*() calls or variants have this called +* automatically for them. This directory is removed automatically on +* blk_release_queue() once the request_queue reference count reaches 0. +*/ +void blk_queue_debugfs_register (struct request_queue *q,const char *name) +{ +queue_debugfs_register_type (q, name,BLK_DBG_DIR_BASE ); +} +EXPORT_SYMBOL_GPL (blk_queue_debugfs_register); + +/** +* blk_queue_debugfs_unregister - remove the debugfs_dir for the block device +* @q: the associated request_queue of the block device +* +* Removes the debugfs_dir for the request_queue on the associated block device. +* This is handled for you on blk_release_queue(), and that should only be +* called once. +* +* Since we don't care where the debugfs_dir was created this is used for all +* types of of enum blk_debugfs_dir_type. +*/ +void blk_queue_debugfs_unregister (struct request_queue *q) +{ +debugfs_remove_recursive (q->debugfs_dir); +} + +static struct dentry *queue_debugfs_symlink_type (struct request_queue *q, +const char *src, +const char *dst, +enum blk_debugfs_dir_type type) +{ +struct dentry *dentry =ERR_PTR (-EINVAL ); +char *dir_dst; + + dir_dst =kzalloc (PATH_MAX ,GFP_KERNEL );
block/blk-debugfs.c line 100
Quoted 10 lines
block/blk-debugfs.c +10
-0+if (!dir_dst) +return dentry; + +switch (type) { +case BLK_DBG_DIR_BASE : +if (dst) +snprintf (dir_dst,PATH_MAX ,"%s" , dst); +else if (!IS_ERR_OR_NULL (q->debugfs_dir)) +snprintf (dir_dst,PATH_MAX ,"%s" , + q->debugfs_dir->d_name.name);
How can debugfs_dir be NULL/error here?
And grabbing the name of a debugfs file is sketchy, just use the name that you think you already have, from the device, don't rely on debugfs working here.
And why a symlink anyway? THat's a new addition, what is going to work with that in userspace?
block/blk-debugfs.c line 104
Quoted 4 lines
block/blk-debugfs.c +4
-0+#ifdef CONFIG_DEBUG_FS + p->debugfs_sym =blk_queue_debugfs_symlink (disk->queue,dev_name (pdev), + disk->disk_name); +#endif
No need to #ifdef this, right?
I feel like this patch series keeps getting more complex and messier over time :(
greg k-h
Re: [PATCH v4 3/5] blktrace: fix debugfs use after free
Quoted 18 lines · Greg KH
Quoted 17 lines · Luis Chamberlain
block/blk-debugfs.c +13
-0+static struct dentry *queue_debugfs_symlink_type (struct request_queue *q, +const char *src, +const char *dst, +enum blk_debugfs_dir_type type) +{ +struct dentry *dentry =ERR_PTR (-EINVAL ); +char *dir_dst; + + dir_dst =kzalloc (PATH_MAX ,GFP_KERNEL ); +if (!dir_dst) +return dentry; + +switch (type) {
Quoted 8 lines
block/blk-debugfs.c line 100
Quoted 6 lines
block/blk-debugfs.c +6
-0+case BLK_DBG_DIR_BASE : +if (dst) +snprintf (dir_dst,PATH_MAX ,"%s" , dst); +else if (!IS_ERR_OR_NULL (q->debugfs_dir)) +snprintf (dir_dst,PATH_MAX ,"%s" , + q->debugfs_dir->d_name.name);How can
debugfs_dirbe NULL/error here?
If someone were to move blk_queue_debugfs_symlink() to sg_add_device(), this can happen as the sd_probe() will run asynchronously. The comment in blk_queue_debugfs_symlink() suggest to be mindful of async probes.
I can do-away with this *iff* we add an probe_complete call to the driver core class interface.
Quoted 5 lines
And grabbing the name of a debugfs file is sketchy, just use the name that you think you already have, from the device, don't rely on debugfs working here.
And why a symlink anyway?
As the commit log explains, and the comment to the calling functions explains, we have a shared request_queue, and this has been the way, the symlink makes that sharing explicit. The next patch explains why using two blktraces on two separate devices would fail, the symlink makes this sharing quite obvious.
It also simplifies the trace/kernel/blktrace.c code to just use the debugfs_dir only.
Quoted 2 lines
THat's a new addition, what is going to work
with that in userspace?
This already works in userspace with blktrace. If you see a regression, please let me know.
Quoted 6 lines
block/blk-debugfs.c
Quoted 4 lines
block/blk-debugfs.c +4
-0+#ifdef CONFIG_DEBUG_FS + p->debugfs_sym =blk_queue_debugfs_symlink (disk->queue,dev_name (pdev), + disk->disk_name); +#endif No need to #ifdef this, right?
It is needed because of the p->debugfs_sym.
Quoted 2 lines
I feel like this patch series keeps getting more complex and messier over time :(
I'm afraid that use of blktrace in scsi-generic is the culprit, the technical debt of the complexities around how the request_queue is shared and properly providing backward compatibilty by removing the debufs_looksup() has caught up to us.
If we were to agree to remove scsi-generic support from blktrace first, that would simplify things hugely.
Luis
PATCH v4 4/5 blktrace: break out of blktrace setup on concurrent calls
In reply to Luis ChamberlainWe use one blktrace per request_queue, that means one per the entire disk. So we cannot run one blktrace on say /dev/vda and then /dev/vda1, or just two calls on /dev/vda.
We check for concurrent setup only at the very end of the blktrace setup though.
If we try to run two concurrent blktraces on the same block device the second one will fail, and the first one seems to go on. However when one tries to kill the first one one will see things like this:
The kernel will show these:
``` debugfs: File 'dropped' in directory 'nvme1n1' already present! debugfs: File 'msg' in directory 'nvme1n1' already present! debugfs: File 'trace0' in directory 'nvme1n1' already present! `` And userspace just sees this error message for the second call: ```
blktrace /dev/nvme1n1
BLKTRACESETUP(2) /dev/nvme1n1 failed: 5/Input/output error ```
The first userspace process #1 will also claim that the files were taken underneath their nose as well. The files are taken away form the first process given that when the second blktrace fails, it will follow up with a BLKTRACESTOP and BLKTRACETEARDOWN. This means that even if go-happy process #1 is waiting for blktrace data, we *have* been asked to take teardown the blktrace.
This can easily be reproduced with break-blktrace [0] run_0005.sh test.
Just break out early if we know we're already going to fail, this will prevent trying to create the files all over again, which we know still exist.
[0] https://github.com/mcgrof/break-blktrace
--- kernel/trace/blktrace.c | 9 +++++++++ 1 file changed, 9 insertions(+)
kernel/trace/blktrace.c +9 -0
@@ -3,6 +3,9 @@* Copyright (C) 2006 Jens Axboe <[email protected]> * */ + +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/kernel.h> #include <linux/blkdev.h> #include <linux/blktrace_api.h>
@@ -493,6 +496,12 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,*/ strreplace (buts->name,'/' ,'_' ); +if (q->blk_trace) { +pr_warn ("Concurrent blktraces are not allowed on %s\n" , + buts->name); +return -EBUSY ; + } + bt =kzalloc (sizeof (*bt),GFP_KERNEL );if (!bt)return -ENOMEM ;
Signature · 1 line
2.25.1
Re: [PATCH v4 4/5] blktrace: break out of blktrace setup on concurrent calls
kernel/trace/blktrace.c static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, · line 507
Quoted 13 lines · Luis Chamberlain
kernel/trace/blktrace.c +6
-0@@ -493,6 +496,12 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,*/ strreplace (buts->name,'/' ,'_' ); +if (q->blk_trace) { +pr_warn ("Concurrent blktraces are not allowed on %s\n" , + buts->name); +return -EBUSY ; + } + bt =kzalloc (sizeof (*bt),GFP_KERNEL );if (!bt)return -ENOMEM ;
Is this really sufficient? Shouldn't concurrent do_blk_trace_setup() calls that refer to the same request queue be serialized to really prevent that debugfs attribute creation fails?
How about using the block device name instead of the partition name in the error message since the concurrency context is the block device and not the partition?
Thanks,
Bart.
Re: [PATCH v4 4/5] blktrace: break out of blktrace setup on concurrent calls
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Luis Chamberlain
kernel/trace/blktrace.c +0
-0@@ -493,6 +496,12 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,*/ strreplace (buts->name,'/' ,'_' );
Quoted 13 lines
kernel/trace/blktrace.c static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, · line 507
Quoted 9 lines
kernel/trace/blktrace.c +6
-0+if (q->blk_trace) { +pr_warn ("Concurrent blktraces are not allowed on %s\n" , + buts->name); +return -EBUSY ; + } + bt =kzalloc (sizeof (*bt),GFP_KERNEL );if (!bt)return -ENOMEM ;Is this really sufficient? Shouldn't concurrent
do_blk_trace_setup()calls that refer to the same request queue be serialized to really prevent that debugfs attribute creation fails?
We'd have to add something like a linked list. Right now I'm just clarifying things which were not clear before. What you describe is a functional feature change. I'm just trying to fix a bug and clarify limitations.
Quoted 3 lines
How about using the block device name instead of the partition name in the error message since the concurrency context is the block device and not the partition?
blk device argument can be NULL here. sg-generic is one case.
Luis
Re: [PATCH v4 4/5] blktrace: break out of blktrace setup on concurrent calls
Quoted 7 lines · Luis Chamberlain
Quoted 3 lines · Bart Van Assche
How about using the block device name instead of the partition name in the error message since the concurrency context is the block device and not the partition?
blk device argument can be NULL here. sg-generic is one case.
I'm going to add a comment about this, as it is easily forgotten.
Luis
PATCH v4 5/5 loop: be paranoid on exit and prevent new additions / removals
In reply to Luis ChamberlainBe pedantic on removal as well and hold the mutex. This should prevent uses of addition while we exit.
I cannot trigger an issue with this though, this is just a fix done through code inspection.
--- drivers/block/loop.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/block/loop.c +4 -0
@@ -2333,6 +2333,8 @@ static void __exit loop_exit(void) range = max_loop ? max_loop << part_shift :1UL <<MINORBITS ; +mutex_lock (&loop_ctl_mutex); +idr_for_each (&loop_index_idr, &loop_exit_cb,NULL );idr_destroy (&loop_index_idr);
@@ -2340,6 +2342,8 @@ static void __exit loop_exit(void)unregister_blkdev (LOOP_MAJOR ,"loop" );misc_deregister (&loop_misc); + +mutex_unlock (&loop_ctl_mutex); }module_init (loop_init);
Signature · 1 line
2.25.1