diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2019-08-20 10:55:16 -0400 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2019-08-20 10:55:16 -0400 |
commit | dc617f29dbe5ef0c8ced65ce62c464af1daaab3d (patch) | |
tree | 3596eb0d334f04f87088c735748bba8e66a71cad | |
parent | 1638045c36772b47a0765f7dca07cb90267e4942 (diff) |
vfs: don't allow writes to swap files
Don't let userspace write to an active swap file because the kernel
effectively has a long term lease on the storage and things could get
seriously corrupted if we let this happen.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
-rw-r--r-- | fs/block_dev.c | 3 | ||||
-rw-r--r-- | include/linux/fs.h | 11 | ||||
-rw-r--r-- | mm/filemap.c | 3 | ||||
-rw-r--r-- | mm/memory.c | 4 | ||||
-rw-r--r-- | mm/mmap.c | 8 | ||||
-rw-r--r-- | mm/swapfile.c | 12 |
6 files changed, 38 insertions, 3 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index c2a85b587922..d9bab63a9b81 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -1978,6 +1978,9 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) | |||
1978 | if (bdev_read_only(I_BDEV(bd_inode))) | 1978 | if (bdev_read_only(I_BDEV(bd_inode))) |
1979 | return -EPERM; | 1979 | return -EPERM; |
1980 | 1980 | ||
1981 | if (IS_SWAPFILE(bd_inode)) | ||
1982 | return -ETXTBSY; | ||
1983 | |||
1981 | if (!iov_iter_count(from)) | 1984 | if (!iov_iter_count(from)) |
1982 | return 0; | 1985 | return 0; |
1983 | 1986 | ||
diff --git a/include/linux/fs.h b/include/linux/fs.h index 56b8e358af5c..a2e3d446ba8e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -3547,4 +3547,15 @@ static inline void simple_fill_fsxattr(struct fsxattr *fa, __u32 xflags) | |||
3547 | fa->fsx_xflags = xflags; | 3547 | fa->fsx_xflags = xflags; |
3548 | } | 3548 | } |
3549 | 3549 | ||
3550 | /* | ||
3551 | * Flush file data before changing attributes. Caller must hold any locks | ||
3552 | * required to prevent further writes to this file until we're done setting | ||
3553 | * flags. | ||
3554 | */ | ||
3555 | static inline int inode_drain_writes(struct inode *inode) | ||
3556 | { | ||
3557 | inode_dio_wait(inode); | ||
3558 | return filemap_write_and_wait(inode->i_mapping); | ||
3559 | } | ||
3560 | |||
3550 | #endif /* _LINUX_FS_H */ | 3561 | #endif /* _LINUX_FS_H */ |
diff --git a/mm/filemap.c b/mm/filemap.c index d0cf700bf201..40667c2f3383 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -2988,6 +2988,9 @@ inline ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from) | |||
2988 | loff_t count; | 2988 | loff_t count; |
2989 | int ret; | 2989 | int ret; |
2990 | 2990 | ||
2991 | if (IS_SWAPFILE(inode)) | ||
2992 | return -ETXTBSY; | ||
2993 | |||
2991 | if (!iov_iter_count(from)) | 2994 | if (!iov_iter_count(from)) |
2992 | return 0; | 2995 | return 0; |
2993 | 2996 | ||
diff --git a/mm/memory.c b/mm/memory.c index e2bb51b6242e..b1dff75640b7 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -2196,6 +2196,10 @@ static vm_fault_t do_page_mkwrite(struct vm_fault *vmf) | |||
2196 | 2196 | ||
2197 | vmf->flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; | 2197 | vmf->flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; |
2198 | 2198 | ||
2199 | if (vmf->vma->vm_file && | ||
2200 | IS_SWAPFILE(vmf->vma->vm_file->f_mapping->host)) | ||
2201 | return VM_FAULT_SIGBUS; | ||
2202 | |||
2199 | ret = vmf->vma->vm_ops->page_mkwrite(vmf); | 2203 | ret = vmf->vma->vm_ops->page_mkwrite(vmf); |
2200 | /* Restore original flags so that caller is not surprised */ | 2204 | /* Restore original flags so that caller is not surprised */ |
2201 | vmf->flags = old_flags; | 2205 | vmf->flags = old_flags; |
@@ -1483,8 +1483,12 @@ unsigned long do_mmap(struct file *file, unsigned long addr, | |||
1483 | case MAP_SHARED_VALIDATE: | 1483 | case MAP_SHARED_VALIDATE: |
1484 | if (flags & ~flags_mask) | 1484 | if (flags & ~flags_mask) |
1485 | return -EOPNOTSUPP; | 1485 | return -EOPNOTSUPP; |
1486 | if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE)) | 1486 | if (prot & PROT_WRITE) { |
1487 | return -EACCES; | 1487 | if (!(file->f_mode & FMODE_WRITE)) |
1488 | return -EACCES; | ||
1489 | if (IS_SWAPFILE(file->f_mapping->host)) | ||
1490 | return -ETXTBSY; | ||
1491 | } | ||
1488 | 1492 | ||
1489 | /* | 1493 | /* |
1490 | * Make sure we don't allow writing to an append-only | 1494 | * Make sure we don't allow writing to an append-only |
diff --git a/mm/swapfile.c b/mm/swapfile.c index a53b7c49b40e..dab43523afdd 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -3275,6 +3275,17 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) | |||
3275 | if (error) | 3275 | if (error) |
3276 | goto bad_swap; | 3276 | goto bad_swap; |
3277 | 3277 | ||
3278 | /* | ||
3279 | * Flush any pending IO and dirty mappings before we start using this | ||
3280 | * swap device. | ||
3281 | */ | ||
3282 | inode->i_flags |= S_SWAPFILE; | ||
3283 | error = inode_drain_writes(inode); | ||
3284 | if (error) { | ||
3285 | inode->i_flags &= ~S_SWAPFILE; | ||
3286 | goto bad_swap; | ||
3287 | } | ||
3288 | |||
3278 | mutex_lock(&swapon_mutex); | 3289 | mutex_lock(&swapon_mutex); |
3279 | prio = -1; | 3290 | prio = -1; |
3280 | if (swap_flags & SWAP_FLAG_PREFER) | 3291 | if (swap_flags & SWAP_FLAG_PREFER) |
@@ -3295,7 +3306,6 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) | |||
3295 | atomic_inc(&proc_poll_event); | 3306 | atomic_inc(&proc_poll_event); |
3296 | wake_up_interruptible(&proc_poll_wait); | 3307 | wake_up_interruptible(&proc_poll_wait); |
3297 | 3308 | ||
3298 | inode->i_flags |= S_SWAPFILE; | ||
3299 | error = 0; | 3309 | error = 0; |
3300 | goto out; | 3310 | goto out; |
3301 | bad_swap: | 3311 | bad_swap: |