Skip to content

Commit ad1e4f7

Browse files
cavokzrafaeljw
authored andcommitted
PM: hibernate: Restrict writes to the resume device
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]>
1 parent c4f39a6 commit ad1e4f7

File tree

3 files changed

+20
-3
lines changed

3 files changed

+20
-3
lines changed

fs/block_dev.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,8 +2023,7 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
20232023
if (bdev_read_only(I_BDEV(bd_inode)))
20242024
return -EPERM;
20252025

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

20302029
if (!iov_iter_count(from))

include/linux/suspend.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,12 @@ static inline bool system_entering_hibernation(void) { return false; }
466466
static inline bool hibernation_available(void) { return false; }
467467
#endif /* CONFIG_HIBERNATION */
468468

469+
#ifdef CONFIG_HIBERNATION_SNAPSHOT_DEV
470+
int is_hibernate_resume_dev(const struct inode *);
471+
#else
472+
static inline int is_hibernate_resume_dev(const struct inode *i) { return 0; }
473+
#endif
474+
469475
/* Hibernation and suspend events */
470476
#define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */
471477
#define PM_POST_HIBERNATION 0x0002 /* Hibernation finished */

kernel/power/user.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,14 @@ static struct snapshot_data {
3535
bool ready;
3636
bool platform_support;
3737
bool free_bitmaps;
38+
struct inode *bd_inode;
3839
} snapshot_state;
3940

41+
int is_hibernate_resume_dev(const struct inode *bd_inode)
42+
{
43+
return hibernation_available() && snapshot_state.bd_inode == bd_inode;
44+
}
45+
4046
static int snapshot_open(struct inode *inode, struct file *filp)
4147
{
4248
struct snapshot_data *data;
@@ -95,6 +101,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
95101
data->frozen = false;
96102
data->ready = false;
97103
data->platform_support = false;
104+
data->bd_inode = NULL;
98105

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

111118
swsusp_free();
112119
data = filp->private_data;
120+
data->bd_inode = NULL;
113121
free_all_swap_pages(data->swap);
114122
if (data->frozen) {
115123
pm_restore_gfp_mask();
@@ -202,6 +210,7 @@ struct compat_resume_swap_area {
202210
static int snapshot_set_swap_area(struct snapshot_data *data,
203211
void __user *argp)
204212
{
213+
struct block_device *bdev;
205214
sector_t offset;
206215
dev_t swdev;
207216

@@ -232,9 +241,12 @@ static int snapshot_set_swap_area(struct snapshot_data *data,
232241
data->swap = -1;
233242
return -EINVAL;
234243
}
235-
data->swap = swap_type_of(swdev, offset, NULL);
244+
data->swap = swap_type_of(swdev, offset, &bdev);
236245
if (data->swap < 0)
237246
return -ENODEV;
247+
248+
data->bd_inode = bdev->bd_inode;
249+
bdput(bdev);
238250
return 0;
239251
}
240252

0 commit comments

Comments
 (0)