aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-07-09 20:12:10 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-07-09 20:12:10 -0400
commit4c0b6c10fbaf0c82efe2a7ba6c236c633d4f2ed7 (patch)
treeba2727168486504a256694aaa5f549bac1aa92eb
parentd5f32af3100165cbd625855bd155b3aa9bd87ebf (diff)
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>
-rw-r--r--Documentation/kernel-parameters.txt3
-rw-r--r--kernel/power/hibernate.c3
-rw-r--r--kernel/power/power.h7
-rw-r--r--kernel/power/snapshot.c42
4 files changed, 55 insertions, 0 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 82b42c958d1c..07960c24642d 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -3594,6 +3594,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
3594 present during boot. 3594 present during boot.
3595 nocompress Don't compress/decompress hibernation images. 3595 nocompress Don't compress/decompress hibernation images.
3596 no Disable hibernation and resume. 3596 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).
3597 3600
3598 retain_initrd [RAM] Keep initrd memory after extraction 3601 retain_initrd [RAM] Keep initrd memory after extraction
3599 3602
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index b00f270d328e..51441d87f0b6 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -1126,6 +1126,9 @@ static int __init hibernate_setup(char *str)
1126 } else if (!strncmp(str, "no", 2)) { 1126 } else if (!strncmp(str, "no", 2)) {
1127 noresume = 1; 1127 noresume = 1;
1128 nohibernate = 1; 1128 nohibernate = 1;
1129 } else if (IS_ENABLED(CONFIG_DEBUG_RODATA)
1130 && !strncmp(str, "protect_image", 13)) {
1131 enable_restore_image_protection();
1129 } 1132 }
1130 return 1; 1133 return 1;
1131} 1134}
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 51f02ecaf125..064963e89194 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -59,6 +59,13 @@ extern int hibernation_snapshot(int platform_mode);
59extern int hibernation_restore(int platform_mode); 59extern int hibernation_restore(int platform_mode);
60extern int hibernation_platform_enter(void); 60extern int hibernation_platform_enter(void);
61 61
62#ifdef CONFIG_DEBUG_RODATA
63/* kernel/power/snapshot.c */
64extern void enable_restore_image_protection(void);
65#else
66static inline void enable_restore_image_protection(void) {}
67#endif /* CONFIG_DEBUG_RODATA */
68
62#else /* !CONFIG_HIBERNATION */ 69#else /* !CONFIG_HIBERNATION */
63 70
64static inline void hibernate_reserved_size_init(void) {} 71static inline void hibernate_reserved_size_init(void) {}
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index d64d5d0efa79..d90df926b59f 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -38,6 +38,43 @@
38 38
39#include "power.h" 39#include "power.h"
40 40
41#ifdef CONFIG_DEBUG_RODATA
42static bool hibernate_restore_protection;
43static bool hibernate_restore_protection_active;
44
45void enable_restore_image_protection(void)
46{
47 hibernate_restore_protection = true;
48}
49
50static inline void hibernate_restore_protection_begin(void)
51{
52 hibernate_restore_protection_active = hibernate_restore_protection;
53}
54
55static inline void hibernate_restore_protection_end(void)
56{
57 hibernate_restore_protection_active = false;
58}
59
60static 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
66static 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
72static inline void hibernate_restore_protection_begin(void) {}
73static inline void hibernate_restore_protection_end(void) {}
74static inline void hibernate_restore_protect_page(void *page_address) {}
75static inline void hibernate_restore_unprotect_page(void *page_address) {}
76#endif /* CONFIG_DEBUG_RODATA */
77
41static int swsusp_page_is_free(struct page *); 78static int swsusp_page_is_free(struct page *);
42static void swsusp_set_page_forbidden(struct page *); 79static void swsusp_set_page_forbidden(struct page *);
43static void swsusp_unset_page_forbidden(struct page *); 80static void swsusp_unset_page_forbidden(struct page *);
@@ -1414,6 +1451,7 @@ loop:
1414 1451
1415 memory_bm_clear_current(forbidden_pages_map); 1452 memory_bm_clear_current(forbidden_pages_map);
1416 memory_bm_clear_current(free_pages_map); 1453 memory_bm_clear_current(free_pages_map);
1454 hibernate_restore_unprotect_page(page_address(page));
1417 __free_page(page); 1455 __free_page(page);
1418 goto loop; 1456 goto loop;
1419 } 1457 }
@@ -1425,6 +1463,7 @@ out:
1425 buffer = NULL; 1463 buffer = NULL;
1426 alloc_normal = 0; 1464 alloc_normal = 0;
1427 alloc_highmem = 0; 1465 alloc_highmem = 0;
1466 hibernate_restore_protection_end();
1428} 1467}
1429 1468
1430/* Helper functions used for the shrinking of memory. */ 1469/* Helper functions used for the shrinking of memory. */
@@ -2548,6 +2587,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
2548 if (error) 2587 if (error)
2549 return error; 2588 return error;
2550 2589
2590 hibernate_restore_protection_begin();
2551 } else if (handle->cur <= nr_meta_pages + 1) { 2591 } else if (handle->cur <= nr_meta_pages + 1) {
2552 error = unpack_orig_pfns(buffer, &copy_bm); 2592 error = unpack_orig_pfns(buffer, &copy_bm);
2553 if (error) 2593 if (error)
@@ -2570,6 +2610,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
2570 copy_last_highmem_page(); 2610 copy_last_highmem_page();
2571 /* Restore page key for data page (s390 only). */ 2611 /* Restore page key for data page (s390 only). */
2572 page_key_write(handle->buffer); 2612 page_key_write(handle->buffer);
2613 hibernate_restore_protect_page(handle->buffer);
2573 handle->buffer = get_buffer(&orig_bm, &ca); 2614 handle->buffer = get_buffer(&orig_bm, &ca);
2574 if (IS_ERR(handle->buffer)) 2615 if (IS_ERR(handle->buffer))
2575 return PTR_ERR(handle->buffer); 2616 return PTR_ERR(handle->buffer);
@@ -2594,6 +2635,7 @@ void snapshot_write_finalize(struct snapshot_handle *handle)
2594 /* Restore page key for data page (s390 only). */ 2635 /* Restore page key for data page (s390 only). */
2595 page_key_write(handle->buffer); 2636 page_key_write(handle->buffer);
2596 page_key_free(); 2637 page_key_free();
2638 hibernate_restore_protect_page(handle->buffer);
2597 /* Do that only if we have loaded the image entirely */ 2639 /* Do that only if we have loaded the image entirely */
2598 if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) { 2640 if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) {
2599 memory_bm_recycle(&orig_bm); 2641 memory_bm_recycle(&orig_bm);