Skip to content

Commit

Permalink
PM / hibernate: Image data protection during restoration
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
rafaeljw committed Jul 10, 2016
1 parent d5f32af commit 4c0b6c1
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3594,6 +3594,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
present during boot.
nocompress Don't compress/decompress hibernation images.
no Disable hibernation and resume.
protect_image Turn on image protection during restoration
(that will set all pages holding image data
during restoration read-only).

retain_initrd [RAM] Keep initrd memory after extraction

Expand Down
3 changes: 3 additions & 0 deletions kernel/power/hibernate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,9 @@ static int __init hibernate_setup(char *str)
} else if (!strncmp(str, "no", 2)) {
noresume = 1;
nohibernate = 1;
} else if (IS_ENABLED(CONFIG_DEBUG_RODATA)
&& !strncmp(str, "protect_image", 13)) {
enable_restore_image_protection();
}
return 1;
}
Expand Down
7 changes: 7 additions & 0 deletions kernel/power/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ extern int hibernation_snapshot(int platform_mode);
extern int hibernation_restore(int platform_mode);
extern int hibernation_platform_enter(void);

#ifdef CONFIG_DEBUG_RODATA
/* kernel/power/snapshot.c */
extern void enable_restore_image_protection(void);
#else
static inline void enable_restore_image_protection(void) {}
#endif /* CONFIG_DEBUG_RODATA */

#else /* !CONFIG_HIBERNATION */

static inline void hibernate_reserved_size_init(void) {}
Expand Down
42 changes: 42 additions & 0 deletions kernel/power/snapshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,43 @@

#include "power.h"

#ifdef CONFIG_DEBUG_RODATA
static bool hibernate_restore_protection;
static bool hibernate_restore_protection_active;

void enable_restore_image_protection(void)
{
hibernate_restore_protection = true;
}

static inline void hibernate_restore_protection_begin(void)
{
hibernate_restore_protection_active = hibernate_restore_protection;
}

static inline void hibernate_restore_protection_end(void)
{
hibernate_restore_protection_active = false;
}

static inline void hibernate_restore_protect_page(void *page_address)
{
if (hibernate_restore_protection_active)
set_memory_ro((unsigned long)page_address, 1);
}

static inline void hibernate_restore_unprotect_page(void *page_address)
{
if (hibernate_restore_protection_active)
set_memory_rw((unsigned long)page_address, 1);
}
#else
static inline void hibernate_restore_protection_begin(void) {}
static inline void hibernate_restore_protection_end(void) {}
static inline void hibernate_restore_protect_page(void *page_address) {}
static inline void hibernate_restore_unprotect_page(void *page_address) {}
#endif /* CONFIG_DEBUG_RODATA */

static int swsusp_page_is_free(struct page *);
static void swsusp_set_page_forbidden(struct page *);
static void swsusp_unset_page_forbidden(struct page *);
Expand Down Expand Up @@ -1414,6 +1451,7 @@ void swsusp_free(void)

memory_bm_clear_current(forbidden_pages_map);
memory_bm_clear_current(free_pages_map);
hibernate_restore_unprotect_page(page_address(page));
__free_page(page);
goto loop;
}
Expand All @@ -1425,6 +1463,7 @@ void swsusp_free(void)
buffer = NULL;
alloc_normal = 0;
alloc_highmem = 0;
hibernate_restore_protection_end();
}

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

hibernate_restore_protection_begin();
} else if (handle->cur <= nr_meta_pages + 1) {
error = unpack_orig_pfns(buffer, &copy_bm);
if (error)
Expand All @@ -2570,6 +2610,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
copy_last_highmem_page();
/* Restore page key for data page (s390 only). */
page_key_write(handle->buffer);
hibernate_restore_protect_page(handle->buffer);
handle->buffer = get_buffer(&orig_bm, &ca);
if (IS_ERR(handle->buffer))
return PTR_ERR(handle->buffer);
Expand All @@ -2594,6 +2635,7 @@ void snapshot_write_finalize(struct snapshot_handle *handle)
/* Restore page key for data page (s390 only). */
page_key_write(handle->buffer);
page_key_free();
hibernate_restore_protect_page(handle->buffer);
/* Do that only if we have loaded the image entirely */
if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) {
memory_bm_recycle(&orig_bm);
Expand Down

0 comments on commit 4c0b6c1

Please sign in to comment.