]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/block_dev.c
serial: mvebu-uart: Fix to avoid a potential NULL pointer dereference
[mirror_ubuntu-bionic-kernel.git] / fs / block_dev.c
index 789f55e851aeffb6b1212403188638d12a1d2540..132868d7a35bd0a68a98fb1e1a028c950e57bc5b 100644 (file)
@@ -54,18 +54,6 @@ struct block_device *I_BDEV(struct inode *inode)
 }
 EXPORT_SYMBOL(I_BDEV);
 
-void __vfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
-{
-       struct va_format vaf;
-       va_list args;
-
-       va_start(args, fmt);
-       vaf.fmt = fmt;
-       vaf.va = &args;
-       printk_ratelimited("%sVFS (%s): %pV\n", prefix, sb->s_id, &vaf);
-       va_end(args);
-}
-
 static void bdev_write_inode(struct block_device *bdev)
 {
        struct inode *inode = bdev->bd_inode;
@@ -116,6 +104,20 @@ void invalidate_bdev(struct block_device *bdev)
 }
 EXPORT_SYMBOL(invalidate_bdev);
 
+static void set_init_blocksize(struct block_device *bdev)
+{
+       unsigned bsize = bdev_logical_block_size(bdev);
+       loff_t size = i_size_read(bdev->bd_inode);
+
+       while (bsize < PAGE_SIZE) {
+               if (size & bsize)
+                       break;
+               bsize <<= 1;
+       }
+       bdev->bd_block_size = bsize;
+       bdev->bd_inode->i_blkbits = blksize_bits(bsize);
+}
+
 int set_blocksize(struct block_device *bdev, int size)
 {
        /* Size must be a power of two, and between 512 and PAGE_SIZE */
@@ -231,7 +233,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter,
 
        ret = bio_iov_iter_get_pages(&bio, iter);
        if (unlikely(ret))
-               return ret;
+               goto out;
        ret = bio.bi_iter.bi_size;
 
        if (iov_iter_rw(iter) == READ) {
@@ -249,7 +251,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter,
                if (!READ_ONCE(bio.bi_private))
                        break;
                if (!(iocb->ki_flags & IOCB_HIPRI) ||
-                   !blk_mq_poll(bdev_get_queue(bdev), qc))
+                   !blk_poll(bdev_get_queue(bdev), qc))
                        io_schedule();
        }
        __set_current_state(TASK_RUNNING);
@@ -260,12 +262,13 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter,
                put_page(bvec->bv_page);
        }
 
-       if (vecs != inline_vecs)
-               kfree(vecs);
-
        if (unlikely(bio.bi_status))
                ret = blk_status_to_errno(bio.bi_status);
 
+out:
+       if (vecs != inline_vecs)
+               kfree(vecs);
+
        bio_uninit(&bio);
 
        return ret;
@@ -414,7 +417,7 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages)
                        break;
 
                if (!(iocb->ki_flags & IOCB_HIPRI) ||
-                   !blk_mq_poll(bdev_get_queue(bdev), qc))
+                   !blk_poll(bdev_get_queue(bdev), qc))
                        io_schedule();
        }
        __set_current_state(TASK_RUNNING);
@@ -674,7 +677,7 @@ int bdev_read_page(struct block_device *bdev, sector_t sector,
        if (!ops->rw_page || bdev_get_integrity(bdev))
                return result;
 
-       result = blk_queue_enter(bdev->bd_queue, false);
+       result = blk_queue_enter(bdev->bd_queue, 0);
        if (result)
                return result;
        result = ops->rw_page(bdev, sector + get_start_sect(bdev), page, false);
@@ -710,7 +713,7 @@ int bdev_write_page(struct block_device *bdev, sector_t sector,
 
        if (!ops->rw_page || bdev_get_integrity(bdev))
                return -EOPNOTSUPP;
-       result = blk_queue_enter(bdev->bd_queue, false);
+       result = blk_queue_enter(bdev->bd_queue, 0);
        if (result)
                return result;
 
@@ -1392,18 +1395,9 @@ EXPORT_SYMBOL(check_disk_change);
 
 void bd_set_size(struct block_device *bdev, loff_t size)
 {
-       unsigned bsize = bdev_logical_block_size(bdev);
-
        inode_lock(bdev->bd_inode);
        i_size_write(bdev->bd_inode, size);
        inode_unlock(bdev->bd_inode);
-       while (bsize < PAGE_SIZE) {
-               if (size & bsize)
-                       break;
-               bsize <<= 1;
-       }
-       bdev->bd_block_size = bsize;
-       bdev->bd_inode->i_blkbits = blksize_bits(bsize);
 }
 EXPORT_SYMBOL(bd_set_size);
 
@@ -1481,8 +1475,10 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
                                }
                        }
 
-                       if (!ret)
+                       if (!ret) {
                                bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
+                               set_init_blocksize(bdev);
+                       }
 
                        /*
                         * If the device is invalidated, rescan partition
@@ -1517,6 +1513,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
                                goto out_clear;
                        }
                        bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9);
+                       set_init_blocksize(bdev);
                }
 
                if (bdev->bd_bdi == &noop_backing_dev_info)
@@ -1672,9 +1669,14 @@ struct block_device *blkdev_get_by_path(const char *path, fmode_t mode,
                                        void *holder)
 {
        struct block_device *bdev;
+       int perm = 0;
        int err;
 
-       bdev = lookup_bdev(path);
+       if (mode & FMODE_READ)
+               perm |= MAY_READ;
+       if (mode & FMODE_WRITE)
+               perm |= MAY_WRITE;
+       bdev = lookup_bdev(path, perm);
        if (IS_ERR(bdev))
                return bdev;
 
@@ -1755,6 +1757,20 @@ static int blkdev_open(struct inode * inode, struct file * filp)
        if (bdev == NULL)
                return -ENOMEM;
 
+       /*
+        * A negative i_writecount for bdev->bd_inode means that the bdev
+        * or one of its paritions is mounted in a user namespace. Deny
+        * writing for non-root in this case, otherwise an unprivileged
+        * user can attack the kernel by modifying the backing store of a
+        * mounted filesystem.
+        */
+       if ((filp->f_mode & FMODE_WRITE) &&
+           !file_ns_capable(filp, &init_user_ns, CAP_SYS_ADMIN) &&
+           !atomic_inc_unless_negative(&bdev->bd_inode->i_writecount)) {
+               bdput(bdev);
+               return -EBUSY;
+       }
+
        filp->f_mapping = bdev->bd_inode->i_mapping;
        filp->f_wb_err = filemap_sample_wb_err(filp->f_mapping);
 
@@ -1851,6 +1867,9 @@ EXPORT_SYMBOL(blkdev_put);
 static int blkdev_close(struct inode * inode, struct file * filp)
 {
        struct block_device *bdev = I_BDEV(bdev_file_inode(filp));
+       if (filp->f_mode & FMODE_WRITE &&
+           !file_ns_capable(filp, &init_user_ns, CAP_SYS_ADMIN))
+               atomic_dec(&bdev->bd_inode->i_writecount);
        blkdev_put(bdev, filp->f_mode);
        return 0;
 }
@@ -2064,12 +2083,14 @@ EXPORT_SYMBOL(ioctl_by_bdev);
 /**
  * lookup_bdev  - lookup a struct block_device by name
  * @pathname:  special file representing the block device
+ * @mask:      rights to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  *
  * Get a reference to the blockdevice at @pathname in the current
  * namespace if possible and return it.  Return ERR_PTR(error)
- * otherwise.
+ * otherwise.  If @mask is non-zero, check for access rights to the
+ * inode at @pathname.
  */
-struct block_device *lookup_bdev(const char *pathname)
+struct block_device *lookup_bdev(const char *pathname, int mask)
 {
        struct block_device *bdev;
        struct inode *inode;
@@ -2084,6 +2105,11 @@ struct block_device *lookup_bdev(const char *pathname)
                return ERR_PTR(error);
 
        inode = d_backing_inode(path.dentry);
+       if (mask != 0 && !capable(CAP_SYS_ADMIN)) {
+               error = __inode_permission(inode, mask);
+               if (error)
+                       goto fail;
+       }
        error = -ENOTBLK;
        if (!S_ISBLK(inode->i_mode))
                goto fail;