blktrace: fix unlocked access to init/start-stop/teardown
authorJens Axboe <axboe@kernel.dk>
Sun, 5 Nov 2017 16:13:48 +0000 (09:13 -0700)
committerJens Axboe <axboe@kernel.dk>
Sat, 11 Nov 2017 02:53:25 +0000 (19:53 -0700)
sg.c calls into the blktrace functions without holding the proper queue
mutex for doing setup, start/stop, or teardown.

Add internal unlocked variants, and export the ones that do the proper
locking.

Fixes: 6da127ad0918 ("blktrace: Add blktrace ioctls to SCSI generic devices")
Tested-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
kernel/trace/blktrace.c

index 45a3928..ea57dd9 100644 (file)
@@ -336,7 +336,7 @@ static void blk_trace_cleanup(struct blk_trace *bt)
                blk_unregister_tracepoints();
 }
 
-int blk_trace_remove(struct request_queue *q)
+static int __blk_trace_remove(struct request_queue *q)
 {
        struct blk_trace *bt;
 
@@ -349,6 +349,17 @@ int blk_trace_remove(struct request_queue *q)
 
        return 0;
 }
+
+int blk_trace_remove(struct request_queue *q)
+{
+       int ret;
+
+       mutex_lock(&q->blk_trace_mutex);
+       ret = __blk_trace_remove(q);
+       mutex_unlock(&q->blk_trace_mutex);
+
+       return ret;
+}
 EXPORT_SYMBOL_GPL(blk_trace_remove);
 
 static ssize_t blk_dropped_read(struct file *filp, char __user *buffer,
@@ -550,9 +561,8 @@ err:
        return ret;
 }
 
-int blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
-                   struct block_device *bdev,
-                   char __user *arg)
+static int __blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
+                            struct block_device *bdev, char __user *arg)
 {
        struct blk_user_trace_setup buts;
        int ret;
@@ -571,6 +581,19 @@ int blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
        }
        return 0;
 }
+
+int blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
+                   struct block_device *bdev,
+                   char __user *arg)
+{
+       int ret;
+
+       mutex_lock(&q->blk_trace_mutex);
+       ret = __blk_trace_setup(q, name, dev, bdev, arg);
+       mutex_unlock(&q->blk_trace_mutex);
+
+       return ret;
+}
 EXPORT_SYMBOL_GPL(blk_trace_setup);
 
 #if defined(CONFIG_COMPAT) && defined(CONFIG_X86_64)
@@ -607,7 +630,7 @@ static int compat_blk_trace_setup(struct request_queue *q, char *name,
 }
 #endif
 
-int blk_trace_startstop(struct request_queue *q, int start)
+static int __blk_trace_startstop(struct request_queue *q, int start)
 {
        int ret;
        struct blk_trace *bt = q->blk_trace;
@@ -646,6 +669,17 @@ int blk_trace_startstop(struct request_queue *q, int start)
 
        return ret;
 }
+
+int blk_trace_startstop(struct request_queue *q, int start)
+{
+       int ret;
+
+       mutex_lock(&q->blk_trace_mutex);
+       ret = __blk_trace_startstop(q, start);
+       mutex_unlock(&q->blk_trace_mutex);
+
+       return ret;
+}
 EXPORT_SYMBOL_GPL(blk_trace_startstop);
 
 /*
@@ -676,7 +710,7 @@ int blk_trace_ioctl(struct block_device *bdev, unsigned cmd, char __user *arg)
        switch (cmd) {
        case BLKTRACESETUP:
                bdevname(bdev, b);
-               ret = blk_trace_setup(q, b, bdev->bd_dev, bdev, arg);
+               ret = __blk_trace_setup(q, b, bdev->bd_dev, bdev, arg);
                break;
 #if defined(CONFIG_COMPAT) && defined(CONFIG_X86_64)
        case BLKTRACESETUP32:
@@ -687,10 +721,10 @@ int blk_trace_ioctl(struct block_device *bdev, unsigned cmd, char __user *arg)
        case BLKTRACESTART:
                start = 1;
        case BLKTRACESTOP:
-               ret = blk_trace_startstop(q, start);
+               ret = __blk_trace_startstop(q, start);
                break;
        case BLKTRACETEARDOWN:
-               ret = blk_trace_remove(q);
+               ret = __blk_trace_remove(q);
                break;
        default:
                ret = -ENOTTY;
@@ -708,10 +742,14 @@ int blk_trace_ioctl(struct block_device *bdev, unsigned cmd, char __user *arg)
  **/
 void blk_trace_shutdown(struct request_queue *q)
 {
+       mutex_lock(&q->blk_trace_mutex);
+
        if (q->blk_trace) {
-               blk_trace_startstop(q, 0);
-               blk_trace_remove(q);
+               __blk_trace_startstop(q, 0);
+               __blk_trace_remove(q);
        }
+
+       mutex_unlock(&q->blk_trace_mutex);
 }
 
 #ifdef CONFIG_BLK_CGROUP