diff mbox series

[2/5] PM / hibernate: Generate and verify signature for snapshot image

Message ID 20190103143227.9138-3-jlee@suse.com (mailing list archive)
State Changes Requested, archived
Headers show
Series Encryption and authentication for hibernate snapshot image | expand

Commit Message

Chun-Yi Lee Jan. 3, 2019, 2:32 p.m. UTC
When producing memory snapshot image, hibernation uses HMAC-SHA512
with snapshot key (from TPM trusted key) to calculate the hash of
all data pages in snapshot image. The hash result will be kept in the
snapshot header as the image signature. Before hibernation restores
image, kernel executes HMAC-SHA512 again and compares the result with
the signature in the header to verify the integrity of snapshot image.

If the verification failed, the resume process will be stopped. Then
the snapshot image will be discarded and system will boot as normal.

On the other hand, a trampoline page be created in snapshot image
when hibernation. This trampoline page be used to forward the state of
snapshot key and the result of snapshot image verification from boot
kernel to image kernel when resuming. The trampoline page will also be
used to forward the snapshot key in the later patch.

Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Chen Yu <yu.c.chen@intel.com>
Cc: Oliver Neukum <oneukum@suse.com>
Cc: Ryan Chen <yu.chen.surf@gmail.com>
Cc: David Howells <dhowells@redhat.com>
Cc: Giovanni Gherdovich <ggherdovich@suse.cz>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Jann Horn <jannh@google.com>
Cc: Andy Lutomirski <luto@kernel.org>
Signed-off-by: "Lee, Chun-Yi" <jlee@suse.com>
---
 kernel/power/hibernate.c |  18 ++-
 kernel/power/power.h     |  26 ++++
 kernel/power/snapshot.c  | 387 +++++++++++++++++++++++++++++++++++++++++++++--
 kernel/power/swap.c      |   6 +
 kernel/power/user.c      |  12 ++
 5 files changed, 432 insertions(+), 17 deletions(-)

Comments

Stephan Mueller Jan. 6, 2019, 8:09 a.m. UTC | #1
Am Donnerstag, 3. Januar 2019, 15:32:24 CET schrieb Lee, Chun-Yi:

Hi Chun,

> +int snapshot_image_verify_decrypt(void)
> +{
> +	int ret, i;
> +
> +	if (!h_buf) {
> +		ret = -ENOMEM;
> +		goto error;
> +	}
> +
> +	ret = snapshot_key_init();
> +	if (ret)
> +		goto error_prep;
> +
> +	ret = snapshot_prepare_hash(true);
> +	if (ret || !s4_verify_desc)
> +		goto error_prep;
> +
> +	for (i = 0; i < nr_copy_pages; i++) {
> +		ret = crypto_shash_update(s4_verify_desc, *(h_buf + i), PAGE_SIZE);
> +		if (ret)
> +			goto error_shash;
> +	}
> +
> +	ret = crypto_shash_final(s4_verify_desc, s4_verify_digest);
> +	if (ret)
> +		goto error_shash;
> +
> +	pr_debug("Signature %*phN\n", SNAPSHOT_DIGEST_SIZE, signature);
> +	pr_debug("Digest    %*phN\n", SNAPSHOT_DIGEST_SIZE, s4_verify_digest);
> +	if (memcmp(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE))
> +		ret = -EKEYREJECTED;
> +
> + error_shash:
> +	snapshot_finish_hash();
> +
> + error_prep:
> +	vfree(h_buf);
> +	if (ret)
> +		pr_warn("Signature verification failed: %d\n", ret);
> + error:
> +	sig_verify_ret = ret;
> +	return ret;
> +}

May I ask why the authentication part is done manually here? Why not using an 
AEAD cipher like the authenc() ciphers, or CCM (I would not recommend GCM 
though)? In this case, the encryption/decryption operation would automatically 
perform the creation of the hash and the verification of the hash. I.e. 
decryption can return EBADMSG which indicates an authentication failure.

> +
> +static int
> +__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap
> *orig_bm) +{
> +	unsigned long pfn, dst_pfn;
> +	struct page *d_page;
> +	void *crypto_buffer = NULL;
> +	int ret = 0;
> +
> +	memory_bm_position_reset(orig_bm);
> +	memory_bm_position_reset(copy_bm);
> +	for (;;) {
> +		pfn = memory_bm_next_pfn(orig_bm);
> +		if (unlikely(pfn == BM_END_OF_MAP))
> +			break;
> +		dst_pfn = memory_bm_next_pfn(copy_bm);
> +		copy_data_page(dst_pfn, pfn);
> +
> +		/* Setup buffer */
> +		d_page = pfn_to_page(dst_pfn);
> +		if (PageHighMem(d_page)) {
> +			void *kaddr = kmap_atomic(d_page);
> +
> +			copy_page(buffer, kaddr);
> +			kunmap_atomic(kaddr);
> +			crypto_buffer = buffer;
> +		} else {
> +			crypto_buffer = page_address(d_page);
> +		}
> +
> +		/* Generate digest */
> +		if (!s4_verify_desc)
> +			continue;
> +		ret = crypto_shash_update(s4_verify_desc, crypto_buffer,
> +					  PAGE_SIZE);

Same here, the creation of the hash would be implicit during the encryption.

Ciao
Stephan
Dan Carpenter Jan. 7, 2019, 6:58 p.m. UTC | #2
Hi Chun-Yi,

url:    https://github.com/0day-ci/linux/commits/Lee-Chun-Yi/Encryption-and-authentication-for-hibernate-snapshot-image/20190104-062004

smatch warnings:
kernel/power/user.c:411 snapshot_ioctl() warn: inconsistent returns 'mutex:&system_transition_mutex'.
  Locked on:   line 265
  Unlocked on: line 213

[ There are some returns missing.  I don't know how/why...  -dan ]

# https://github.com/0day-ci/linux/commit/1e583ff7a095d508d78bea4d35a916594c2fc23c
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 1e583ff7a095d508d78bea4d35a916594c2fc23c
vim +411 kernel/power/user.c

6e1819d61 Rafael J. Wysocki 2006-03-23  203  
52d11025d Alan Cox          2008-06-11  204  static long snapshot_ioctl(struct file *filp, unsigned int cmd,
52d11025d Alan Cox          2008-06-11  205  							unsigned long arg)
6e1819d61 Rafael J. Wysocki 2006-03-23  206  {
6e1819d61 Rafael J. Wysocki 2006-03-23  207  	int error = 0;
6e1819d61 Rafael J. Wysocki 2006-03-23  208  	struct snapshot_data *data;
af508b34d Rafael J. Wysocki 2007-10-26  209  	loff_t size;
3aef83e0e Rafael J. Wysocki 2006-12-06  210  	sector_t offset;
6e1819d61 Rafael J. Wysocki 2006-03-23  211  
6e1819d61 Rafael J. Wysocki 2006-03-23  212  	if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
6e1819d61 Rafael J. Wysocki 2006-03-23  213  		return -ENOTTY;
6e1819d61 Rafael J. Wysocki 2006-03-23  214  	if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
6e1819d61 Rafael J. Wysocki 2006-03-23  215  		return -ENOTTY;
6e1819d61 Rafael J. Wysocki 2006-03-23  216  	if (!capable(CAP_SYS_ADMIN))
6e1819d61 Rafael J. Wysocki 2006-03-23  217  		return -EPERM;
6e1819d61 Rafael J. Wysocki 2006-03-23  218  
55f2503c3 Pingfan Liu       2018-07-31  219  	if (!mutex_trylock(&system_transition_mutex))
25f2f3daa Rafael J. Wysocki 2008-06-11  220  		return -EBUSY;
6e1819d61 Rafael J. Wysocki 2006-03-23  221  
942f40155 Rafael J. Wysocki 2013-08-30  222  	lock_device_hotplug();
25f2f3daa Rafael J. Wysocki 2008-06-11  223  	data = filp->private_data;
52d11025d Alan Cox          2008-06-11  224  
6e1819d61 Rafael J. Wysocki 2006-03-23  225  	switch (cmd) {
6e1819d61 Rafael J. Wysocki 2006-03-23  226  
6e1819d61 Rafael J. Wysocki 2006-03-23  227  	case SNAPSHOT_FREEZE:
6e1819d61 Rafael J. Wysocki 2006-03-23  228  		if (data->frozen)
6e1819d61 Rafael J. Wysocki 2006-03-23  229  			break;
1bfcf1304 Rafael J. Wysocki 2008-10-15  230  
232b14328 Rafael J. Wysocki 2007-10-18  231  		printk("Syncing filesystems ... ");
70f68ee81 Dominik Brodowski 2018-03-14  232  		ksys_sync();
232b14328 Rafael J. Wysocki 2007-10-18  233  		printk("done.\n");
232b14328 Rafael J. Wysocki 2007-10-18  234  
1bfcf1304 Rafael J. Wysocki 2008-10-15  235  		error = freeze_processes();
8fd37a4c9 Rafael J. Wysocki 2013-08-30  236  		if (error)
8fd37a4c9 Rafael J. Wysocki 2013-08-30  237  			break;
8fd37a4c9 Rafael J. Wysocki 2013-08-30  238  
8fd37a4c9 Rafael J. Wysocki 2013-08-30  239  		error = create_basic_memory_bitmaps();
8fd37a4c9 Rafael J. Wysocki 2013-08-30  240  		if (error)
8fd37a4c9 Rafael J. Wysocki 2013-08-30  241  			thaw_processes();
8fd37a4c9 Rafael J. Wysocki 2013-08-30  242  		else
7bc9b1cff Rafael J. Wysocki 2013-10-18  243  			data->frozen = true;
8fd37a4c9 Rafael J. Wysocki 2013-08-30  244  
6e1819d61 Rafael J. Wysocki 2006-03-23  245  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  246  
6e1819d61 Rafael J. Wysocki 2006-03-23  247  	case SNAPSHOT_UNFREEZE:
2f41dddbb Rafael J. Wysocki 2007-06-16  248  		if (!data->frozen || data->ready)
6e1819d61 Rafael J. Wysocki 2006-03-23  249  			break;
c9e664f1f Rafael J. Wysocki 2010-12-03  250  		pm_restore_gfp_mask();
1e583ff7a Lee, Chun-Yi      2019-01-03  251  		snapshot_restore_trampoline();
8fd37a4c9 Rafael J. Wysocki 2013-08-30  252  		free_basic_memory_bitmaps();
aab172891 Rafael J. Wysocki 2013-09-30  253  		data->free_bitmaps = false;
6e1819d61 Rafael J. Wysocki 2006-03-23  254  		thaw_processes();
7bc9b1cff Rafael J. Wysocki 2013-10-18  255  		data->frozen = false;
6e1819d61 Rafael J. Wysocki 2006-03-23  256  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  257  
b694e52eb Jiri Slaby        2010-01-27  258  	case SNAPSHOT_CREATE_IMAGE:
6e1819d61 Rafael J. Wysocki 2006-03-23  259  		if (data->mode != O_RDONLY || !data->frozen  || data->ready) {
6e1819d61 Rafael J. Wysocki 2006-03-23  260  			error = -EPERM;
6e1819d61 Rafael J. Wysocki 2006-03-23  261  			break;
6e1819d61 Rafael J. Wysocki 2006-03-23  262  		}
1e583ff7a Lee, Chun-Yi      2019-01-03  263  		error = snapshot_key_init();
1e583ff7a Lee, Chun-Yi      2019-01-03  264  		if (error)
1e583ff7a Lee, Chun-Yi      2019-01-03  265  			return error;
                                                                ^^^^^^^^^^^^^

1e583ff7a Lee, Chun-Yi      2019-01-03  266  		error = snapshot_create_trampoline();
1e583ff7a Lee, Chun-Yi      2019-01-03  267  		if (error)
1e583ff7a Lee, Chun-Yi      2019-01-03  268  			return error;
                                                                ^^^^^^^^^^^^^
These should be break statements.

c9e664f1f Rafael J. Wysocki 2010-12-03  269  		pm_restore_gfp_mask();
eb57c1cf0 Rafael J. Wysocki 2007-10-26  270  		error = hibernation_snapshot(data->platform_support);
51d6ff7ac Srivatsa S. Bhat  2012-02-04  271  		if (!error) {
cc5d207c8 Rafael J. Wysocki 2007-10-26  272  			error = put_user(in_suspend, (int __user *)arg);
a556d5b58 Srivatsa S. Bhat  2012-02-04  273  			data->ready = !freezer_test_done && !error;
97819a262 Srivatsa S. Bhat  2011-12-01  274  			freezer_test_done = false;
97819a262 Srivatsa S. Bhat  2011-12-01  275  		}
6e1819d61 Rafael J. Wysocki 2006-03-23  276  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  277  
6e1819d61 Rafael J. Wysocki 2006-03-23  278  	case SNAPSHOT_ATOMIC_RESTORE:
8357376d3 Rafael J. Wysocki 2006-12-06  279  		snapshot_write_finalize(&data->handle);
6e1819d61 Rafael J. Wysocki 2006-03-23  280  		if (data->mode != O_WRONLY || !data->frozen ||
6e1819d61 Rafael J. Wysocki 2006-03-23  281  		    !snapshot_image_loaded(&data->handle)) {
6e1819d61 Rafael J. Wysocki 2006-03-23  282  			error = -EPERM;
6e1819d61 Rafael J. Wysocki 2006-03-23  283  			break;
6e1819d61 Rafael J. Wysocki 2006-03-23  284  		}
1e583ff7a Lee, Chun-Yi      2019-01-03  285  		if (snapshot_image_verify_decrypt()) {
1e583ff7a Lee, Chun-Yi      2019-01-03  286  			error = -EPERM;
1e583ff7a Lee, Chun-Yi      2019-01-03  287  			break;
1e583ff7a Lee, Chun-Yi      2019-01-03  288  		}
1e583ff7a Lee, Chun-Yi      2019-01-03  289  		snapshot_init_trampoline();
eb57c1cf0 Rafael J. Wysocki 2007-10-26  290  		error = hibernation_restore(data->platform_support);
6e1819d61 Rafael J. Wysocki 2006-03-23  291  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  292  
6e1819d61 Rafael J. Wysocki 2006-03-23  293  	case SNAPSHOT_FREE:
6e1819d61 Rafael J. Wysocki 2006-03-23  294  		swsusp_free();
6e1819d61 Rafael J. Wysocki 2006-03-23  295  		memset(&data->handle, 0, sizeof(struct snapshot_handle));
7bc9b1cff Rafael J. Wysocki 2013-10-18  296  		data->ready = false;
181e9bdef Rafael J. Wysocki 2012-01-29  297  		/*
181e9bdef Rafael J. Wysocki 2012-01-29  298  		 * It is necessary to thaw kernel threads here, because
181e9bdef Rafael J. Wysocki 2012-01-29  299  		 * SNAPSHOT_CREATE_IMAGE may be invoked directly after
181e9bdef Rafael J. Wysocki 2012-01-29  300  		 * SNAPSHOT_FREE.  In that case, if kernel threads were not
181e9bdef Rafael J. Wysocki 2012-01-29  301  		 * thawed, the preallocation of memory carried out by
181e9bdef Rafael J. Wysocki 2012-01-29  302  		 * hibernation_snapshot() might run into problems (i.e. it
181e9bdef Rafael J. Wysocki 2012-01-29  303  		 * might fail or even deadlock).
181e9bdef Rafael J. Wysocki 2012-01-29  304  		 */
181e9bdef Rafael J. Wysocki 2012-01-29  305  		thaw_kernel_threads();
6e1819d61 Rafael J. Wysocki 2006-03-23  306  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  307  
b694e52eb Jiri Slaby        2010-01-27  308  	case SNAPSHOT_PREF_IMAGE_SIZE:
6e1819d61 Rafael J. Wysocki 2006-03-23  309  		image_size = arg;
6e1819d61 Rafael J. Wysocki 2006-03-23  310  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  311  
af508b34d Rafael J. Wysocki 2007-10-26  312  	case SNAPSHOT_GET_IMAGE_SIZE:
af508b34d Rafael J. Wysocki 2007-10-26  313  		if (!data->ready) {
af508b34d Rafael J. Wysocki 2007-10-26  314  			error = -ENODATA;
af508b34d Rafael J. Wysocki 2007-10-26  315  			break;
af508b34d Rafael J. Wysocki 2007-10-26  316  		}
af508b34d Rafael J. Wysocki 2007-10-26  317  		size = snapshot_get_image_size();
af508b34d Rafael J. Wysocki 2007-10-26  318  		size <<= PAGE_SHIFT;
af508b34d Rafael J. Wysocki 2007-10-26  319  		error = put_user(size, (loff_t __user *)arg);
af508b34d Rafael J. Wysocki 2007-10-26  320  		break;
af508b34d Rafael J. Wysocki 2007-10-26  321  
b694e52eb Jiri Slaby        2010-01-27  322  	case SNAPSHOT_AVAIL_SWAP_SIZE:
af508b34d Rafael J. Wysocki 2007-10-26  323  		size = count_swap_pages(data->swap, 1);
af508b34d Rafael J. Wysocki 2007-10-26  324  		size <<= PAGE_SHIFT;
af508b34d Rafael J. Wysocki 2007-10-26  325  		error = put_user(size, (loff_t __user *)arg);
6e1819d61 Rafael J. Wysocki 2006-03-23  326  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  327  
b694e52eb Jiri Slaby        2010-01-27  328  	case SNAPSHOT_ALLOC_SWAP_PAGE:
6e1819d61 Rafael J. Wysocki 2006-03-23  329  		if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
6e1819d61 Rafael J. Wysocki 2006-03-23  330  			error = -ENODEV;
6e1819d61 Rafael J. Wysocki 2006-03-23  331  			break;
6e1819d61 Rafael J. Wysocki 2006-03-23  332  		}
d1d241cc2 Rafael J. Wysocki 2007-05-06  333  		offset = alloc_swapdev_block(data->swap);
6e1819d61 Rafael J. Wysocki 2006-03-23  334  		if (offset) {
6e1819d61 Rafael J. Wysocki 2006-03-23  335  			offset <<= PAGE_SHIFT;
cc5d207c8 Rafael J. Wysocki 2007-10-26  336  			error = put_user(offset, (loff_t __user *)arg);
6e1819d61 Rafael J. Wysocki 2006-03-23  337  		} else {
6e1819d61 Rafael J. Wysocki 2006-03-23  338  			error = -ENOSPC;
6e1819d61 Rafael J. Wysocki 2006-03-23  339  		}
6e1819d61 Rafael J. Wysocki 2006-03-23  340  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  341  
6e1819d61 Rafael J. Wysocki 2006-03-23  342  	case SNAPSHOT_FREE_SWAP_PAGES:
6e1819d61 Rafael J. Wysocki 2006-03-23  343  		if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
6e1819d61 Rafael J. Wysocki 2006-03-23  344  			error = -ENODEV;
6e1819d61 Rafael J. Wysocki 2006-03-23  345  			break;
6e1819d61 Rafael J. Wysocki 2006-03-23  346  		}
d1d241cc2 Rafael J. Wysocki 2007-05-06  347  		free_all_swap_pages(data->swap);
6e1819d61 Rafael J. Wysocki 2006-03-23  348  		break;
6e1819d61 Rafael J. Wysocki 2006-03-23  349  
9b238205b Luca Tettamanti   2006-03-23  350  	case SNAPSHOT_S2RAM:
9b238205b Luca Tettamanti   2006-03-23  351  		if (!data->frozen) {
9b238205b Luca Tettamanti   2006-03-23  352  			error = -EPERM;
9b238205b Luca Tettamanti   2006-03-23  353  			break;
9b238205b Luca Tettamanti   2006-03-23  354  		}
6c961dfb7 Rafael J. Wysocki 2007-07-19  355  		/*
6c961dfb7 Rafael J. Wysocki 2007-07-19  356  		 * Tasks are frozen and the notifiers have been called with
6c961dfb7 Rafael J. Wysocki 2007-07-19  357  		 * PM_HIBERNATION_PREPARE
6c961dfb7 Rafael J. Wysocki 2007-07-19  358  		 */
6c961dfb7 Rafael J. Wysocki 2007-07-19  359  		error = suspend_devices_and_enter(PM_SUSPEND_MEM);
7bc9b1cff Rafael J. Wysocki 2013-10-18  360  		data->ready = false;
9b238205b Luca Tettamanti   2006-03-23  361  		break;
9b238205b Luca Tettamanti   2006-03-23  362  
eb57c1cf0 Rafael J. Wysocki 2007-10-26  363  	case SNAPSHOT_PLATFORM_SUPPORT:
eb57c1cf0 Rafael J. Wysocki 2007-10-26  364  		data->platform_support = !!arg;
eb57c1cf0 Rafael J. Wysocki 2007-10-26  365  		break;
eb57c1cf0 Rafael J. Wysocki 2007-10-26  366  
eb57c1cf0 Rafael J. Wysocki 2007-10-26  367  	case SNAPSHOT_POWER_OFF:
eb57c1cf0 Rafael J. Wysocki 2007-10-26  368  		if (data->platform_support)
eb57c1cf0 Rafael J. Wysocki 2007-10-26  369  			error = hibernation_platform_enter();
eb57c1cf0 Rafael J. Wysocki 2007-10-26  370  		break;
eb57c1cf0 Rafael J. Wysocki 2007-10-26  371  
37b2ba12d Rafael J. Wysocki 2006-12-06  372  	case SNAPSHOT_SET_SWAP_AREA:
d1d241cc2 Rafael J. Wysocki 2007-05-06  373  		if (swsusp_swap_in_use()) {
37b2ba12d Rafael J. Wysocki 2006-12-06  374  			error = -EPERM;
37b2ba12d Rafael J. Wysocki 2006-12-06  375  		} else {
37b2ba12d Rafael J. Wysocki 2006-12-06  376  			struct resume_swap_area swap_area;
37b2ba12d Rafael J. Wysocki 2006-12-06  377  			dev_t swdev;
37b2ba12d Rafael J. Wysocki 2006-12-06  378  
37b2ba12d Rafael J. Wysocki 2006-12-06  379  			error = copy_from_user(&swap_area, (void __user *)arg,
37b2ba12d Rafael J. Wysocki 2006-12-06  380  					sizeof(struct resume_swap_area));
37b2ba12d Rafael J. Wysocki 2006-12-06  381  			if (error) {
37b2ba12d Rafael J. Wysocki 2006-12-06  382  				error = -EFAULT;
37b2ba12d Rafael J. Wysocki 2006-12-06  383  				break;
37b2ba12d Rafael J. Wysocki 2006-12-06  384  			}
37b2ba12d Rafael J. Wysocki 2006-12-06  385  
37b2ba12d Rafael J. Wysocki 2006-12-06  386  			/*
37b2ba12d Rafael J. Wysocki 2006-12-06  387  			 * User space encodes device types as two-byte values,
37b2ba12d Rafael J. Wysocki 2006-12-06  388  			 * so we need to recode them
37b2ba12d Rafael J. Wysocki 2006-12-06  389  			 */
d88d4050d Jiri Slaby        2010-04-10  390  			swdev = new_decode_dev(swap_area.dev);
37b2ba12d Rafael J. Wysocki 2006-12-06  391  			if (swdev) {
37b2ba12d Rafael J. Wysocki 2006-12-06  392  				offset = swap_area.offset;
7bf236874 Rafael J. Wysocki 2007-01-05  393  				data->swap = swap_type_of(swdev, offset, NULL);
37b2ba12d Rafael J. Wysocki 2006-12-06  394  				if (data->swap < 0)
37b2ba12d Rafael J. Wysocki 2006-12-06  395  					error = -ENODEV;
37b2ba12d Rafael J. Wysocki 2006-12-06  396  			} else {
37b2ba12d Rafael J. Wysocki 2006-12-06  397  				data->swap = -1;
37b2ba12d Rafael J. Wysocki 2006-12-06  398  				error = -EINVAL;
37b2ba12d Rafael J. Wysocki 2006-12-06  399  			}
37b2ba12d Rafael J. Wysocki 2006-12-06  400  		}
37b2ba12d Rafael J. Wysocki 2006-12-06  401  		break;
37b2ba12d Rafael J. Wysocki 2006-12-06  402  
6e1819d61 Rafael J. Wysocki 2006-03-23  403  	default:
6e1819d61 Rafael J. Wysocki 2006-03-23  404  		error = -ENOTTY;
6e1819d61 Rafael J. Wysocki 2006-03-23  405  
6e1819d61 Rafael J. Wysocki 2006-03-23  406  	}
25f2f3daa Rafael J. Wysocki 2008-06-11  407  
942f40155 Rafael J. Wysocki 2013-08-30  408  	unlock_device_hotplug();
55f2503c3 Pingfan Liu       2018-07-31  409  	mutex_unlock(&system_transition_mutex);
25f2f3daa Rafael J. Wysocki 2008-06-11  410  
6e1819d61 Rafael J. Wysocki 2006-03-23 @411  	return error;
6e1819d61 Rafael J. Wysocki 2006-03-23  412  }
6e1819d61 Rafael J. Wysocki 2006-03-23  413  

:::::: The code at line 411 was first introduced by commit
:::::: 6e1819d615f24ce0726a7d0bd3dd0152d7b21654 [PATCH] swsusp: userland interface

:::::: TO: Rafael J. Wysocki <rjw@sisk.pl>
:::::: CC: Linus Torvalds <torvalds@g5.osdl.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox series

Patch

diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index ecc31e8e40d0..0dda6a9f0af1 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -271,10 +271,14 @@  static int create_image(int platform_mode)
 {
 	int error;
 
+	error = snapshot_prepare_hash(false);
+	if (error)
+		return error;
+
 	error = dpm_suspend_end(PMSG_FREEZE);
 	if (error) {
 		pr_err("Some devices failed to power down, aborting hibernation\n");
-		return error;
+		goto finish_hash;
 	}
 
 	error = platform_pre_snapshot(platform_mode);
@@ -331,6 +335,9 @@  static int create_image(int platform_mode)
 	dpm_resume_start(in_suspend ?
 		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
 
+ finish_hash:
+	snapshot_finish_hash();
+
 	return error;
 }
 
@@ -694,6 +701,14 @@  int hibernate(void)
 		return -EPERM;
 	}
 
+	error = snapshot_key_init();
+	if (error)
+		return error;
+
+	error = snapshot_create_trampoline();
+	if (error)
+		return error;
+
 	lock_system_sleep();
 	/* The snapshot device should not be opened while we're running */
 	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
@@ -750,6 +765,7 @@  int hibernate(void)
 		pm_restore_gfp_mask();
 	} else {
 		pm_pr_dbg("Image restored successfully.\n");
+		snapshot_restore_trampoline();
 	}
 
  Free_bitmaps:
diff --git a/kernel/power/power.h b/kernel/power/power.h
index fe2dfa0d4d36..c614b0a294e3 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -11,6 +11,10 @@ 
 #define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE
 #define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE
 
+/* HMAC algorithm for hibernate snapshot signature */
+#define SNAPSHOT_HMAC	"hmac(sha512)"
+#define SNAPSHOT_DIGEST_SIZE	SHA512_DIGEST_SIZE
+
 struct swsusp_info {
 	struct new_utsname	uts;
 	u32			version_code;
@@ -19,6 +23,17 @@  struct swsusp_info {
 	unsigned long		image_pages;
 	unsigned long		pages;
 	unsigned long		size;
+	unsigned long		trampoline_pfn;
+	u8			signature[SNAPSHOT_DIGEST_SIZE];
+} __aligned(PAGE_SIZE);
+
+/*
+ * The trampoline page is used to forward information
+ * from boot kernel to image kernel in restore stage.
+ */
+struct trampoline {
+	bool snapshot_key_valid;
+	int sig_verify_ret;
 } __aligned(PAGE_SIZE);
 
 #ifdef CONFIG_HIBERNATION
@@ -27,12 +42,19 @@  extern void __init hibernate_reserved_size_init(void);
 extern void __init hibernate_image_size_init(void);
 
 #ifdef CONFIG_HIBERNATION_ENC_AUTH
+/* kernel/power/snapshot.c */
+extern int snapshot_image_verify_decrypt(void);
+extern int snapshot_prepare_hash(bool may_sleep);
+extern void snapshot_finish_hash(void);
 /* kernel/power/snapshot_key.c */
 extern int snapshot_key_init(void);
 extern bool snapshot_key_initialized(void);
 extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep);
 extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep);
 #else
+static inline int snapshot_image_verify_decrypt(void) { return 0; }
+static inline int snapshot_prepare_hash(bool may_sleep) { return 0; }
+static inline void snapshot_finish_hash(void) {}
 static inline int snapshot_key_init(void) { return 0; }
 #endif	/* !CONFIG_HIBERNATION_ENC_AUTH */
 
@@ -171,6 +193,10 @@  extern int snapshot_read_next(struct snapshot_handle *handle);
 extern int snapshot_write_next(struct snapshot_handle *handle);
 extern void snapshot_write_finalize(struct snapshot_handle *handle);
 extern int snapshot_image_loaded(struct snapshot_handle *handle);
+extern int snapshot_create_trampoline(void);
+extern void snapshot_init_trampoline(void);
+extern void snapshot_restore_trampoline(void);
+extern void snapshot_free_trampoline(void);
 
 /* If unset, the snapshot device cannot be open. */
 extern atomic_t snapshot_device_available;
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 640b2034edd6..e817c035f378 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -33,12 +33,16 @@ 
 #include <linux/compiler.h>
 #include <linux/ktime.h>
 #include <linux/set_memory.h>
+#include <linux/vmalloc.h>
 
 #include <linux/uaccess.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 #include <asm/io.h>
+#ifdef CONFIG_HIBERNATION_ENC_AUTH
+#include <crypto/hash.h>
+#endif
 
 #include "power.h"
 
@@ -79,6 +83,15 @@  static inline void hibernate_restore_protect_page(void *page_address) {}
 static inline void hibernate_restore_unprotect_page(void *page_address) {}
 #endif /* CONFIG_STRICT_KERNEL_RWX  && CONFIG_ARCH_HAS_SET_MEMORY */
 
+/* the trampoline is used by image kernel */
+static void *trampoline_virt;
+
+/* trampoline pfn from swsusp_info in snapshot for snapshot_write_next() */
+static unsigned long trampoline_pfn;
+
+/* Keep the buffer for foward page in snapshot_write_next() */
+static void *trampoline_buff;
+
 static int swsusp_page_is_free(struct page *);
 static void swsusp_set_page_forbidden(struct page *);
 static void swsusp_unset_page_forbidden(struct page *);
@@ -1393,8 +1406,246 @@  static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn)
 }
 #endif /* CONFIG_HIGHMEM */
 
-static void copy_data_pages(struct memory_bitmap *copy_bm,
-			    struct memory_bitmap *orig_bm)
+/* Total number of image pages */
+static unsigned int nr_copy_pages;
+
+/* Point array for collecting buffers' address in snapshot_write_next() */
+static void **h_buf;
+
+#ifdef CONFIG_HIBERNATION_ENC_AUTH
+/*
+ * Signature of snapshot image
+ */
+static u8 signature[SNAPSHOT_DIGEST_SIZE];
+
+/* Keep the signature verification result for trampoline */
+static int sig_verify_ret;
+
+/* keep the snapshot key status for trampoline */
+static bool snapshot_key_valid;
+
+static u8 *s4_verify_digest;
+static struct shash_desc *s4_verify_desc;
+
+int snapshot_prepare_hash(bool may_sleep)
+{
+	char auth_key[DERIVED_KEY_SIZE];
+	struct crypto_shash *tfm;
+	size_t digest_size, desc_size;
+	int ret;
+
+	ret = snapshot_get_auth_key(auth_key, may_sleep);
+	if (ret) {
+		pr_warn_once("auth key is invalid: %d\n", ret);
+		return -EINVAL;
+	}
+	snapshot_key_valid = true;
+
+	tfm = crypto_alloc_shash(SNAPSHOT_HMAC, 0, 0);
+	if (IS_ERR(tfm)) {
+		pr_err("Allocate HMAC failed: %ld\n", PTR_ERR(tfm));
+		return PTR_ERR(tfm);
+	}
+
+	ret = crypto_shash_setkey(tfm, auth_key, DERIVED_KEY_SIZE);
+	if (ret) {
+		pr_err("Set HMAC key failed\n");
+		goto error;
+	}
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*s4_verify_desc);
+	digest_size = crypto_shash_digestsize(tfm);
+	s4_verify_digest = kzalloc(digest_size + desc_size,
+				   may_sleep ? GFP_KERNEL : GFP_ATOMIC);
+	if (!s4_verify_digest) {
+		pr_err("Allocate digest failed\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	s4_verify_desc = (void *) s4_verify_digest + digest_size;
+	s4_verify_desc->tfm = tfm;
+	if (may_sleep)
+		s4_verify_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+	ret = crypto_shash_init(s4_verify_desc);
+	if (ret < 0)
+		goto free_shash;
+
+	return 0;
+
+ free_shash:
+	kfree(s4_verify_digest);
+ error:
+	crypto_free_shash(tfm);
+	s4_verify_digest = NULL;
+	s4_verify_desc = NULL;
+	return ret;
+}
+
+void snapshot_finish_hash(void)
+{
+	if (s4_verify_desc)
+		crypto_free_shash(s4_verify_desc->tfm);
+	kfree(s4_verify_digest);
+	s4_verify_desc = NULL;
+	s4_verify_digest = NULL;
+}
+
+int snapshot_image_verify_decrypt(void)
+{
+	int ret, i;
+
+	if (!h_buf) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ret = snapshot_key_init();
+	if (ret)
+		goto error_prep;
+
+	ret = snapshot_prepare_hash(true);
+	if (ret || !s4_verify_desc)
+		goto error_prep;
+
+	for (i = 0; i < nr_copy_pages; i++) {
+		ret = crypto_shash_update(s4_verify_desc, *(h_buf + i), PAGE_SIZE);
+		if (ret)
+			goto error_shash;
+	}
+
+	ret = crypto_shash_final(s4_verify_desc, s4_verify_digest);
+	if (ret)
+		goto error_shash;
+
+	pr_debug("Signature %*phN\n", SNAPSHOT_DIGEST_SIZE, signature);
+	pr_debug("Digest    %*phN\n", SNAPSHOT_DIGEST_SIZE, s4_verify_digest);
+	if (memcmp(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE))
+		ret = -EKEYREJECTED;
+
+ error_shash:
+	snapshot_finish_hash();
+
+ error_prep:
+	vfree(h_buf);
+	if (ret)
+		pr_warn("Signature verification failed: %d\n", ret);
+ error:
+	sig_verify_ret = ret;
+	return ret;
+}
+
+static int
+__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
+{
+	unsigned long pfn, dst_pfn;
+	struct page *d_page;
+	void *crypto_buffer = NULL;
+	int ret = 0;
+
+	memory_bm_position_reset(orig_bm);
+	memory_bm_position_reset(copy_bm);
+	for (;;) {
+		pfn = memory_bm_next_pfn(orig_bm);
+		if (unlikely(pfn == BM_END_OF_MAP))
+			break;
+		dst_pfn = memory_bm_next_pfn(copy_bm);
+		copy_data_page(dst_pfn, pfn);
+
+		/* Setup buffer */
+		d_page = pfn_to_page(dst_pfn);
+		if (PageHighMem(d_page)) {
+			void *kaddr = kmap_atomic(d_page);
+
+			copy_page(buffer, kaddr);
+			kunmap_atomic(kaddr);
+			crypto_buffer = buffer;
+		} else {
+			crypto_buffer = page_address(d_page);
+		}
+
+		/* Generate digest */
+		if (!s4_verify_desc)
+			continue;
+		ret = crypto_shash_update(s4_verify_desc, crypto_buffer,
+					  PAGE_SIZE);
+		if (ret)
+			return ret;
+	}
+
+	if (s4_verify_desc) {
+		ret = crypto_shash_final(s4_verify_desc, s4_verify_digest);
+		if (ret)
+			return ret;
+
+		memset(signature, 0, SNAPSHOT_DIGEST_SIZE);
+		memcpy(signature, s4_verify_digest, SNAPSHOT_DIGEST_SIZE);
+	}
+
+	return 0;
+}
+
+static void alloc_h_buf(void)
+{
+	h_buf = vmalloc(sizeof(void *) * nr_copy_pages);
+	if (!h_buf)
+		pr_err("Allocate buffer point array failed\n");
+}
+
+static void init_signature(struct swsusp_info *info)
+{
+	memcpy(info->signature, signature, SNAPSHOT_DIGEST_SIZE);
+}
+
+static void load_signature(struct swsusp_info *info)
+{
+	memset(signature, 0, SNAPSHOT_DIGEST_SIZE);
+	memcpy(signature, info->signature, SNAPSHOT_DIGEST_SIZE);
+}
+
+static void init_sig_verify(struct trampoline *t)
+{
+	t->sig_verify_ret = sig_verify_ret;
+	t->snapshot_key_valid = snapshot_key_valid;
+	sig_verify_ret = 0;
+	snapshot_key_valid = 0;
+}
+
+static void handle_sig_verify(struct trampoline *t)
+{
+	if (t->sig_verify_ret)
+		pr_warn("Signature verification failed: %d\n",
+			t->sig_verify_ret);
+	else if (t->snapshot_key_valid)
+		pr_info("Signature verification passed.\n");
+}
+#else
+static int
+__copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
+{
+	unsigned long pfn;
+
+	memory_bm_position_reset(orig_bm);
+	memory_bm_position_reset(copy_bm);
+	for (;;) {
+		pfn = memory_bm_next_pfn(orig_bm);
+		if (unlikely(pfn == BM_END_OF_MAP))
+			break;
+		copy_data_page(memory_bm_next_pfn(copy_bm), pfn);
+	}
+
+	return 0;
+}
+
+static inline void alloc_h_buf(void) {}
+static inline void init_signature(struct swsusp_info *info) {}
+static inline void load_signature(struct swsusp_info *info) {}
+static inline void init_sig_verify(struct trampoline *t) {}
+static inline void handle_sig_verify(struct trampoline *t) {}
+#endif /* !CONFIG_HIBERNATION_ENC_AUTH */
+
+static int copy_data_pages(struct memory_bitmap *copy_bm,
+			   struct memory_bitmap *orig_bm)
 {
 	struct zone *zone;
 	unsigned long pfn;
@@ -1408,18 +1659,9 @@  static void copy_data_pages(struct memory_bitmap *copy_bm,
 			if (page_is_saveable(zone, pfn))
 				memory_bm_set_bit(orig_bm, pfn);
 	}
-	memory_bm_position_reset(orig_bm);
-	memory_bm_position_reset(copy_bm);
-	for(;;) {
-		pfn = memory_bm_next_pfn(orig_bm);
-		if (unlikely(pfn == BM_END_OF_MAP))
-			break;
-		copy_data_page(memory_bm_next_pfn(copy_bm), pfn);
-	}
+	return __copy_data_pages(copy_bm, orig_bm);
 }
 
-/* Total number of image pages */
-static unsigned int nr_copy_pages;
 /* Number of pages needed for saving the original pfns of the image pages */
 static unsigned int nr_meta_pages;
 /*
@@ -1961,6 +2203,7 @@  static int swsusp_alloc(struct memory_bitmap *copy_bm,
 asmlinkage __visible int swsusp_save(void)
 {
 	unsigned int nr_pages, nr_highmem;
+	int ret;
 
 	pr_info("Creating hibernation image:\n");
 
@@ -1984,7 +2227,11 @@  asmlinkage __visible int swsusp_save(void)
 	 * Kill them.
 	 */
 	drain_local_pages(NULL);
-	copy_data_pages(&copy_bm, &orig_bm);
+	ret = copy_data_pages(&copy_bm, &orig_bm);
+	if (ret) {
+		pr_err("Copy data pages failed\n");
+		return ret;
+	}
 
 	/*
 	 * End of critical section. From now on, we can write to memory,
@@ -2038,10 +2285,98 @@  static int init_header(struct swsusp_info *info)
 	info->pages = snapshot_get_image_size();
 	info->size = info->pages;
 	info->size <<= PAGE_SHIFT;
+	info->trampoline_pfn = page_to_pfn(virt_to_page(trampoline_virt));
+	init_signature(info);
 	return init_header_complete(info);
 }
 
 /**
+ * create trampoline - Create a trampoline page before snapshot be created
+ * In hibernation process, this routine will be called by kernel before
+ * the snapshot image be created. It can be used in resuming process.
+ */
+int snapshot_create_trampoline(void)
+{
+	if (trampoline_virt) {
+		pr_warn("Tried to create trampoline again\n");
+		return 0;
+	}
+
+	trampoline_virt = (void *)get_zeroed_page(GFP_KERNEL);
+	if (!trampoline_virt) {
+		pr_err("Allocate trampoline page failed\n");
+		return -ENOMEM;
+	}
+	trampoline_pfn = 0;
+	trampoline_buff = NULL;
+
+	return 0;
+}
+
+/**
+ * initial trampoline - Put data to trampoline buffer for target kernel
+ *
+ * In resuming process, this routine will be called by boot kernel before
+ * the target kernel be restored. The boot kernel uses trampoline buffer
+ * to transfer information to target kernel.
+ */
+void snapshot_init_trampoline(void)
+{
+	struct trampoline *t;
+
+	if (!trampoline_pfn || !trampoline_buff) {
+		pr_err("Did not find trampoline buffer, pfn: %ld\n",
+			trampoline_pfn);
+		return;
+	}
+
+	hibernate_restore_unprotect_page(trampoline_buff);
+	memset(trampoline_buff, 0, PAGE_SIZE);
+	t = (struct trampoline *)trampoline_buff;
+
+	init_sig_verify(t);
+
+	pr_info("Hibernation trampoline page prepared\n");
+}
+
+/**
+ * restore trampoline - Handle the data from boot kernel and free.
+ *
+ * In resuming process, this routine will be called by target kernel
+ * after target kernel is restored. The target kernel handles
+ * the data in trampoline that it is transferred from boot kernel.
+ */
+void snapshot_restore_trampoline(void)
+{
+	struct trampoline *t;
+
+	if (!trampoline_virt) {
+		pr_err("Doesn't have trampoline page\n");
+		return;
+	}
+
+	t = (struct trampoline *)trampoline_virt;
+
+	handle_sig_verify(t);
+	snapshot_free_trampoline();
+}
+
+void snapshot_free_trampoline(void)
+{
+	if (!trampoline_virt) {
+		pr_err("No trampoline page can be freed\n");
+		return;
+	}
+
+	trampoline_pfn = 0;
+	trampoline_buff = NULL;
+	memset(trampoline_virt, 0, PAGE_SIZE);
+	free_page((unsigned long)trampoline_virt);
+	trampoline_virt = NULL;
+	pr_info("Trampoline freed\n");
+}
+
+/**
  * pack_pfns - Prepare PFNs for saving.
  * @bm: Memory bitmap.
  * @buf: Memory buffer to store the PFNs in.
@@ -2188,6 +2523,8 @@  static int load_header(struct swsusp_info *info)
 	if (!error) {
 		nr_copy_pages = info->image_pages;
 		nr_meta_pages = info->pages - info->image_pages - 1;
+		trampoline_pfn = info->trampoline_pfn;
+		load_signature(info);
 	}
 	return error;
 }
@@ -2521,7 +2858,8 @@  static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm)
  * Get the address that snapshot_write_next() should return to its caller to
  * write to.
  */
-static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
+static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca,
+			unsigned long *pfn_out)
 {
 	struct pbe *pbe;
 	struct page *page;
@@ -2530,6 +2868,9 @@  static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
 	if (pfn == BM_END_OF_MAP)
 		return ERR_PTR(-EFAULT);
 
+	if (pfn_out)
+		*pfn_out = pfn;
+
 	page = pfn_to_page(pfn);
 	if (PageHighMem(page))
 		return get_highmem_page_buffer(page, ca);
@@ -2577,6 +2918,7 @@  static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)
 int snapshot_write_next(struct snapshot_handle *handle)
 {
 	static struct chain_allocator ca;
+	unsigned long pfn;
 	int error = 0;
 
 	/* Check if we have already loaded the entire image */
@@ -2601,6 +2943,12 @@  int snapshot_write_next(struct snapshot_handle *handle)
 
 		safe_pages_list = NULL;
 
+		/* Allocate buffer point array for generating
+		 * digest to compare with signature.
+		 * h_buf will freed in snapshot_image_verify_decrypt().
+		 */
+		alloc_h_buf();
+
 		error = memory_bm_create(&copy_bm, GFP_ATOMIC, PG_ANY);
 		if (error)
 			return error;
@@ -2624,21 +2972,28 @@  int snapshot_write_next(struct snapshot_handle *handle)
 			chain_init(&ca, GFP_ATOMIC, PG_SAFE);
 			memory_bm_position_reset(&orig_bm);
 			restore_pblist = NULL;
-			handle->buffer = get_buffer(&orig_bm, &ca);
+			handle->buffer = get_buffer(&orig_bm, &ca, &pfn);
 			handle->sync_read = 0;
 			if (IS_ERR(handle->buffer))
 				return PTR_ERR(handle->buffer);
+			if (h_buf)
+				*h_buf = handle->buffer;
 		}
 	} else {
 		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);
+		handle->buffer = get_buffer(&orig_bm, &ca, &pfn);
 		if (IS_ERR(handle->buffer))
 			return PTR_ERR(handle->buffer);
 		if (handle->buffer != buffer)
 			handle->sync_read = 0;
+		/* Capture the trampoline for transfer data */
+		if (pfn == trampoline_pfn && trampoline_pfn)
+			trampoline_buff = handle->buffer;
+		if (h_buf)
+			*(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer;
 	}
 	handle->cur++;
 	return PAGE_SIZE;
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index d7f6c1a288d3..2e669f589830 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -1095,6 +1095,9 @@  static int load_image(struct swap_map_handle *handle,
 		snapshot_write_finalize(snapshot);
 		if (!snapshot_image_loaded(snapshot))
 			ret = -ENODATA;
+		if (!ret)
+			ret = snapshot_image_verify_decrypt();
+		snapshot_init_trampoline();
 	}
 	swsusp_show_speed(start, stop, nr_to_read, "Read");
 	return ret;
@@ -1447,6 +1450,9 @@  static int load_image_lzo(struct swap_map_handle *handle,
 				}
 			}
 		}
+		if (!ret)
+			ret = snapshot_image_verify_decrypt();
+		snapshot_init_trampoline();
 	}
 	swsusp_show_speed(start, stop, nr_to_read, "Read");
 out_clean:
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 2d8b60a3c86b..d5c8f777e8d8 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -248,6 +248,7 @@  static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 		if (!data->frozen || data->ready)
 			break;
 		pm_restore_gfp_mask();
+		snapshot_restore_trampoline();
 		free_basic_memory_bitmaps();
 		data->free_bitmaps = false;
 		thaw_processes();
@@ -259,6 +260,12 @@  static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 			error = -EPERM;
 			break;
 		}
+		error = snapshot_key_init();
+		if (error)
+			return error;
+		error = snapshot_create_trampoline();
+		if (error)
+			return error;
 		pm_restore_gfp_mask();
 		error = hibernation_snapshot(data->platform_support);
 		if (!error) {
@@ -275,6 +282,11 @@  static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 			error = -EPERM;
 			break;
 		}
+		if (snapshot_image_verify_decrypt()) {
+			error = -EPERM;
+			break;
+		}
+		snapshot_init_trampoline();
 		error = hibernation_restore(data->platform_support);
 		break;