Skip to content

Commit 4c0b6c1

Browse files
committed
PM / hibernate: Image data protection during restoration
Make it possible to protect all pages holding image data during hibernate image restoration by setting them read-only (so as to catch attempts to write to those pages after image data have been stored in them). This adds overhead to image restoration code (it may cause large page mappings to be split as a result of page flags changes) and the errors it protects against should never happen in theory, so the feature is only active after passing hibernate=protect_image to the command line of the restore kernel. Also it only is built if CONFIG_DEBUG_RODATA is set. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent d5f32af commit 4c0b6c1

File tree

4 files changed

+55
-0
lines changed

4 files changed

+55
-0
lines changed

Documentation/kernel-parameters.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3594,6 +3594,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
35943594
present during boot.
35953595
nocompress Don't compress/decompress hibernation images.
35963596
no Disable hibernation and resume.
3597+
protect_image Turn on image protection during restoration
3598+
(that will set all pages holding image data
3599+
during restoration read-only).
35973600

35983601
retain_initrd [RAM] Keep initrd memory after extraction
35993602

kernel/power/hibernate.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,9 @@ static int __init hibernate_setup(char *str)
11261126
} else if (!strncmp(str, "no", 2)) {
11271127
noresume = 1;
11281128
nohibernate = 1;
1129+
} else if (IS_ENABLED(CONFIG_DEBUG_RODATA)
1130+
&& !strncmp(str, "protect_image", 13)) {
1131+
enable_restore_image_protection();
11291132
}
11301133
return 1;
11311134
}

kernel/power/power.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ extern int hibernation_snapshot(int platform_mode);
5959
extern int hibernation_restore(int platform_mode);
6060
extern int hibernation_platform_enter(void);
6161

62+
#ifdef CONFIG_DEBUG_RODATA
63+
/* kernel/power/snapshot.c */
64+
extern void enable_restore_image_protection(void);
65+
#else
66+
static inline void enable_restore_image_protection(void) {}
67+
#endif /* CONFIG_DEBUG_RODATA */
68+
6269
#else /* !CONFIG_HIBERNATION */
6370

6471
static inline void hibernate_reserved_size_init(void) {}

kernel/power/snapshot.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,43 @@
3838

3939
#include "power.h"
4040

41+
#ifdef CONFIG_DEBUG_RODATA
42+
static bool hibernate_restore_protection;
43+
static bool hibernate_restore_protection_active;
44+
45+
void enable_restore_image_protection(void)
46+
{
47+
hibernate_restore_protection = true;
48+
}
49+
50+
static inline void hibernate_restore_protection_begin(void)
51+
{
52+
hibernate_restore_protection_active = hibernate_restore_protection;
53+
}
54+
55+
static inline void hibernate_restore_protection_end(void)
56+
{
57+
hibernate_restore_protection_active = false;
58+
}
59+
60+
static inline void hibernate_restore_protect_page(void *page_address)
61+
{
62+
if (hibernate_restore_protection_active)
63+
set_memory_ro((unsigned long)page_address, 1);
64+
}
65+
66+
static inline void hibernate_restore_unprotect_page(void *page_address)
67+
{
68+
if (hibernate_restore_protection_active)
69+
set_memory_rw((unsigned long)page_address, 1);
70+
}
71+
#else
72+
static inline void hibernate_restore_protection_begin(void) {}
73+
static inline void hibernate_restore_protection_end(void) {}
74+
static inline void hibernate_restore_protect_page(void *page_address) {}
75+
static inline void hibernate_restore_unprotect_page(void *page_address) {}
76+
#endif /* CONFIG_DEBUG_RODATA */
77+
4178
static int swsusp_page_is_free(struct page *);
4279
static void swsusp_set_page_forbidden(struct page *);
4380
static void swsusp_unset_page_forbidden(struct page *);
@@ -1414,6 +1451,7 @@ void swsusp_free(void)
14141451

14151452
memory_bm_clear_current(forbidden_pages_map);
14161453
memory_bm_clear_current(free_pages_map);
1454+
hibernate_restore_unprotect_page(page_address(page));
14171455
__free_page(page);
14181456
goto loop;
14191457
}
@@ -1425,6 +1463,7 @@ void swsusp_free(void)
14251463
buffer = NULL;
14261464
alloc_normal = 0;
14271465
alloc_highmem = 0;
1466+
hibernate_restore_protection_end();
14281467
}
14291468

14301469
/* Helper functions used for the shrinking of memory. */
@@ -2548,6 +2587,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
25482587
if (error)
25492588
return error;
25502589

2590+
hibernate_restore_protection_begin();
25512591
} else if (handle->cur <= nr_meta_pages + 1) {
25522592
error = unpack_orig_pfns(buffer, &copy_bm);
25532593
if (error)
@@ -2570,6 +2610,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
25702610
copy_last_highmem_page();
25712611
/* Restore page key for data page (s390 only). */
25722612
page_key_write(handle->buffer);
2613+
hibernate_restore_protect_page(handle->buffer);
25732614
handle->buffer = get_buffer(&orig_bm, &ca);
25742615
if (IS_ERR(handle->buffer))
25752616
return PTR_ERR(handle->buffer);
@@ -2594,6 +2635,7 @@ void snapshot_write_finalize(struct snapshot_handle *handle)
25942635
/* Restore page key for data page (s390 only). */
25952636
page_key_write(handle->buffer);
25962637
page_key_free();
2638+
hibernate_restore_protect_page(handle->buffer);
25972639
/* Do that only if we have loaded the image entirely */
25982640
if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) {
25992641
memory_bm_recycle(&orig_bm);

0 commit comments

Comments
 (0)