Skip to content

Commit

Permalink
PM: hibernate: Restrict writes to the resume device
Browse files Browse the repository at this point in the history
Hibernation via snapshot device requires write permission to the swap
block device, the one that more often (but not necessarily) is used to
store the hibernation image.

With this patch, such permissions are granted iff:

 1) snapshot device config option is enabled
 2) swap partition is used as resume device

In other circumstances the swap device is not writable from userspace.

In order to achieve this, every write attempt to a swap device is
checked against the device configured as part of the uswsusp API [0]
using a pointer to the inode struct in memory. If the swap device being
written was not configured for resuming, the write request is denied.

NOTE: this implementation works only for swap block devices, where the
inode configured by swapon (which sets S_SWAPFILE) is the same used
by SNAPSHOT_SET_SWAP_AREA.

In case of swap file, SNAPSHOT_SET_SWAP_AREA indeed receives the inode
of the block device containing the filesystem where the swap file is
located (+ offset in it) which is never passed to swapon and then has
not set S_SWAPFILE.

As result, the swap file itself (as a file) has never an option to be
written from userspace. Instead it remains writable if accessed directly
from the containing block device, which is always writeable from root.

[0] Documentation/power/userland-swsusp.rst

v2:
 - rename is_hibernate_snapshot_dev() to is_hibernate_resume_dev()
 - fix description so to correctly refer to the resume device

Signed-off-by: Domenico Andreoli <[email protected]>
Acked-by: Darrick J. Wong <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
  • Loading branch information
cavokz authored and rafaeljw committed May 27, 2020
1 parent c4f39a6 commit ad1e4f7
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 3 deletions.
3 changes: 1 addition & 2 deletions fs/block_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2023,8 +2023,7 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (bdev_read_only(I_BDEV(bd_inode)))
return -EPERM;

/* uswsusp needs write permission to the swap */
if (IS_SWAPFILE(bd_inode) && !hibernation_available())
if (IS_SWAPFILE(bd_inode) && !is_hibernate_resume_dev(bd_inode))
return -ETXTBSY;

if (!iov_iter_count(from))
Expand Down
6 changes: 6 additions & 0 deletions include/linux/suspend.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,12 @@ static inline bool system_entering_hibernation(void) { return false; }
static inline bool hibernation_available(void) { return false; }
#endif /* CONFIG_HIBERNATION */

#ifdef CONFIG_HIBERNATION_SNAPSHOT_DEV
int is_hibernate_resume_dev(const struct inode *);
#else
static inline int is_hibernate_resume_dev(const struct inode *i) { return 0; }
#endif

/* Hibernation and suspend events */
#define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */
#define PM_POST_HIBERNATION 0x0002 /* Hibernation finished */
Expand Down
14 changes: 13 additions & 1 deletion kernel/power/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ static struct snapshot_data {
bool ready;
bool platform_support;
bool free_bitmaps;
struct inode *bd_inode;
} snapshot_state;

int is_hibernate_resume_dev(const struct inode *bd_inode)
{
return hibernation_available() && snapshot_state.bd_inode == bd_inode;
}

static int snapshot_open(struct inode *inode, struct file *filp)
{
struct snapshot_data *data;
Expand Down Expand Up @@ -95,6 +101,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
data->frozen = false;
data->ready = false;
data->platform_support = false;
data->bd_inode = NULL;

Unlock:
unlock_system_sleep();
Expand All @@ -110,6 +117,7 @@ static int snapshot_release(struct inode *inode, struct file *filp)

swsusp_free();
data = filp->private_data;
data->bd_inode = NULL;
free_all_swap_pages(data->swap);
if (data->frozen) {
pm_restore_gfp_mask();
Expand Down Expand Up @@ -202,6 +210,7 @@ struct compat_resume_swap_area {
static int snapshot_set_swap_area(struct snapshot_data *data,
void __user *argp)
{
struct block_device *bdev;
sector_t offset;
dev_t swdev;

Expand Down Expand Up @@ -232,9 +241,12 @@ static int snapshot_set_swap_area(struct snapshot_data *data,
data->swap = -1;
return -EINVAL;
}
data->swap = swap_type_of(swdev, offset, NULL);
data->swap = swap_type_of(swdev, offset, &bdev);
if (data->swap < 0)
return -ENODEV;

data->bd_inode = bdev->bd_inode;
bdput(bdev);
return 0;
}

Expand Down

0 comments on commit ad1e4f7

Please sign in to comment.