[RFC PATCH 2/3] PM: hibernate: permit encrypted snapshot device under lockdown

From: Sean Rhodes

Date: Mon Jul 13 2026 - 11:45:09 EST


Lockdown disables hibernation because restoring attacker-controlled image
data can modify kernel memory. Permit opening the userspace snapshot
device under lockdown when encrypted hibernation support is built, but
require encryption to be enabled before image data can be read or written
and before restore or power-off ioctls can proceed.

Tested with W=1 object builds of kernel/power/snapenc.o,
kernel/power/hibernate.o, and kernel/power/user.o at this commit.

Signed-off-by: Sean Rhodes <sean@starlabs.systems>
---
kernel/power/hibernate.c | 8 ++++++++
kernel/power/power.h | 1 +
kernel/power/user.c | 36 ++++++++++++++++++++++++++++++++++--
kernel/power/user.h | 1 +
4 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index a95cb447a13a..0b8626bdf81a 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -114,6 +114,14 @@ bool hibernation_available(void)
!secretmem_active() && !cxl_mem_active();
}

+bool hibernation_snapshot_dev_available(void)
+{
+ return nohibernate == 0 &&
+ (!security_locked_down(LOCKDOWN_HIBERNATION) ||
+ IS_ENABLED(CONFIG_ENCRYPTED_HIBERNATION)) &&
+ !secretmem_active() && !cxl_mem_active();
+}
+
/**
* hibernation_set_ops - Set the global hibernate operations.
* @ops: Hibernation operations to use in subsequent hibernation transitions.
diff --git a/kernel/power/power.h b/kernel/power/power.h
index e080230f97fc..68ed191b905c 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -168,6 +168,7 @@ extern int snapshot_image_loaded(struct snapshot_handle *handle);

extern bool hibernate_acquire(void);
extern void hibernate_release(void);
+bool hibernation_snapshot_dev_available(void);

extern sector_t alloc_swapdev_block(int swap);
extern void free_all_swap_pages(int swap);
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 10a61a9f5df0..d30aed4bc35f 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -21,6 +21,7 @@
#include <linux/console.h>
#include <linux/cpu.h>
#include <linux/freezer.h>
+#include <linux/security.h>

#include <linux/uaccess.h>

@@ -32,7 +33,12 @@ struct snapshot_data snapshot_state;

int is_hibernate_resume_dev(dev_t dev)
{
- return hibernation_available() && snapshot_state.dev == dev;
+ return hibernation_snapshot_dev_available() && snapshot_state.dev == dev;
+}
+
+static bool snapshot_encryption_required(void)
+{
+ return security_locked_down(LOCKDOWN_HIBERNATION);
}

static int snapshot_open(struct inode *inode, struct file *filp)
@@ -41,7 +47,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
unsigned int sleep_flags;
int error;

- if (!hibernation_available())
+ if (!hibernation_snapshot_dev_available())
return -EPERM;

sleep_flags = lock_system_sleep();
@@ -90,6 +96,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)
data->ready = false;
data->platform_support = false;
data->dev = 0;
+ data->encryption_required = snapshot_encryption_required();

unlock:
unlock_system_sleep(sleep_flags);
@@ -141,6 +148,10 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf,
res = -ENODATA;
goto unlock;
}
+ if (data->encryption_required && !snapshot_encryption_enabled(data)) {
+ res = -EPERM;
+ goto unlock;
+ }

if (snapshot_encryption_enabled(data)) {
res = snapshot_read_encrypted(data, buf, count, offp);
@@ -183,6 +194,11 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,

data = filp->private_data;

+ if (data->encryption_required && !snapshot_encryption_enabled(data)) {
+ res = -EPERM;
+ goto unlock;
+ }
+
if (snapshot_encryption_enabled(data)) {
res = snapshot_write_encrypted(data, buf, count, offp);
goto unlock;
@@ -321,6 +337,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = -EPERM;
break;
}
+ if (data->encryption_required && !snapshot_encryption_enabled(data)) {
+ error = -EPERM;
+ break;
+ }
pm_restore_gfp_mask();
error = hibernation_snapshot(data->platform_support);
if (!error) {
@@ -331,6 +351,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
break;

case SNAPSHOT_ATOMIC_RESTORE:
+ if (data->encryption_required && !snapshot_encryption_enabled(data)) {
+ error = -EPERM;
+ break;
+ }
if (snapshot_encryption_enabled(data)) {
error = snapshot_finalize_decrypted_image(data);
if (error)
@@ -415,6 +439,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = -EPERM;
break;
}
+ if (data->encryption_required && !snapshot_encryption_enabled(data)) {
+ error = -EPERM;
+ break;
+ }
/*
* Tasks are frozen and the notifiers have been called with
* PM_HIBERNATION_PREPARE
@@ -428,6 +456,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
break;

case SNAPSHOT_POWER_OFF:
+ if (data->encryption_required && !snapshot_encryption_enabled(data)) {
+ error = -EPERM;
+ break;
+ }
if (data->platform_support)
error = hibernation_platform_enter();
break;
diff --git a/kernel/power/user.h b/kernel/power/user.h
index e13a0ac439f3..3c3498bdb752 100644
--- a/kernel/power/user.h
+++ b/kernel/power/user.h
@@ -20,6 +20,7 @@ struct snapshot_data {
bool ready;
bool platform_support;
bool free_bitmaps;
+ bool encryption_required;
dev_t dev;

#if defined(CONFIG_ENCRYPTED_HIBERNATION)
--
2.53.0