162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Intel Memory Protection Keys management
462306a36Sopenharmony_ci * Copyright (c) 2015, Intel Corporation.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/debugfs.h>		/* debugfs_create_u32()		*/
762306a36Sopenharmony_ci#include <linux/mm_types.h>             /* mm_struct, vma, etc...       */
862306a36Sopenharmony_ci#include <linux/pkeys.h>                /* PKEY_*                       */
962306a36Sopenharmony_ci#include <uapi/asm-generic/mman-common.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/cpufeature.h>             /* boot_cpu_has, ...            */
1262306a36Sopenharmony_ci#include <asm/mmu_context.h>            /* vma_pkey()                   */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciint __execute_only_pkey(struct mm_struct *mm)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	bool need_to_set_mm_pkey = false;
1762306a36Sopenharmony_ci	int execute_only_pkey = mm->context.execute_only_pkey;
1862306a36Sopenharmony_ci	int ret;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	/* Do we need to assign a pkey for mm's execute-only maps? */
2162306a36Sopenharmony_ci	if (execute_only_pkey == -1) {
2262306a36Sopenharmony_ci		/* Go allocate one to use, which might fail */
2362306a36Sopenharmony_ci		execute_only_pkey = mm_pkey_alloc(mm);
2462306a36Sopenharmony_ci		if (execute_only_pkey < 0)
2562306a36Sopenharmony_ci			return -1;
2662306a36Sopenharmony_ci		need_to_set_mm_pkey = true;
2762306a36Sopenharmony_ci	}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	/*
3062306a36Sopenharmony_ci	 * We do not want to go through the relatively costly
3162306a36Sopenharmony_ci	 * dance to set PKRU if we do not need to.  Check it
3262306a36Sopenharmony_ci	 * first and assume that if the execute-only pkey is
3362306a36Sopenharmony_ci	 * write-disabled that we do not have to set it
3462306a36Sopenharmony_ci	 * ourselves.
3562306a36Sopenharmony_ci	 */
3662306a36Sopenharmony_ci	if (!need_to_set_mm_pkey &&
3762306a36Sopenharmony_ci	    !__pkru_allows_read(read_pkru(), execute_only_pkey)) {
3862306a36Sopenharmony_ci		return execute_only_pkey;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 * Set up PKRU so that it denies access for everything
4362306a36Sopenharmony_ci	 * other than execution.
4462306a36Sopenharmony_ci	 */
4562306a36Sopenharmony_ci	ret = arch_set_user_pkey_access(current, execute_only_pkey,
4662306a36Sopenharmony_ci			PKEY_DISABLE_ACCESS);
4762306a36Sopenharmony_ci	/*
4862306a36Sopenharmony_ci	 * If the PKRU-set operation failed somehow, just return
4962306a36Sopenharmony_ci	 * 0 and effectively disable execute-only support.
5062306a36Sopenharmony_ci	 */
5162306a36Sopenharmony_ci	if (ret) {
5262306a36Sopenharmony_ci		mm_set_pkey_free(mm, execute_only_pkey);
5362306a36Sopenharmony_ci		return -1;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* We got one, store it and use it from here on out */
5762306a36Sopenharmony_ci	if (need_to_set_mm_pkey)
5862306a36Sopenharmony_ci		mm->context.execute_only_pkey = execute_only_pkey;
5962306a36Sopenharmony_ci	return execute_only_pkey;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	/* Do this check first since the vm_flags should be hot */
6562306a36Sopenharmony_ci	if ((vma->vm_flags & VM_ACCESS_FLAGS) != VM_EXEC)
6662306a36Sopenharmony_ci		return false;
6762306a36Sopenharmony_ci	if (vma_pkey(vma) != vma->vm_mm->context.execute_only_pkey)
6862306a36Sopenharmony_ci		return false;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return true;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*
7462306a36Sopenharmony_ci * This is only called for *plain* mprotect calls.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ciint __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, int pkey)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	/*
7962306a36Sopenharmony_ci	 * Is this an mprotect_pkey() call?  If so, never
8062306a36Sopenharmony_ci	 * override the value that came from the user.
8162306a36Sopenharmony_ci	 */
8262306a36Sopenharmony_ci	if (pkey != -1)
8362306a36Sopenharmony_ci		return pkey;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * The mapping is execute-only.  Go try to get the
8762306a36Sopenharmony_ci	 * execute-only protection key.  If we fail to do that,
8862306a36Sopenharmony_ci	 * fall through as if we do not have execute-only
8962306a36Sopenharmony_ci	 * support in this mm.
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci	if (prot == PROT_EXEC) {
9262306a36Sopenharmony_ci		pkey = execute_only_pkey(vma->vm_mm);
9362306a36Sopenharmony_ci		if (pkey > 0)
9462306a36Sopenharmony_ci			return pkey;
9562306a36Sopenharmony_ci	} else if (vma_is_pkey_exec_only(vma)) {
9662306a36Sopenharmony_ci		/*
9762306a36Sopenharmony_ci		 * Protections are *not* PROT_EXEC, but the mapping
9862306a36Sopenharmony_ci		 * is using the exec-only pkey.  This mapping was
9962306a36Sopenharmony_ci		 * PROT_EXEC and will no longer be.  Move back to
10062306a36Sopenharmony_ci		 * the default pkey.
10162306a36Sopenharmony_ci		 */
10262306a36Sopenharmony_ci		return ARCH_DEFAULT_PKEY;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/*
10662306a36Sopenharmony_ci	 * This is a vanilla, non-pkey mprotect (or we failed to
10762306a36Sopenharmony_ci	 * setup execute-only), inherit the pkey from the VMA we
10862306a36Sopenharmony_ci	 * are working on.
10962306a36Sopenharmony_ci	 */
11062306a36Sopenharmony_ci	return vma_pkey(vma);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define PKRU_AD_MASK(pkey)	(PKRU_AD_BIT << ((pkey) * PKRU_BITS_PER_PKEY))
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * Make the default PKRU value (at execve() time) as restrictive
11762306a36Sopenharmony_ci * as possible.  This ensures that any threads clone()'d early
11862306a36Sopenharmony_ci * in the process's lifetime will not accidentally get access
11962306a36Sopenharmony_ci * to data which is pkey-protected later on.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ciu32 init_pkru_value = PKRU_AD_MASK( 1) | PKRU_AD_MASK( 2) |
12262306a36Sopenharmony_ci		      PKRU_AD_MASK( 3) | PKRU_AD_MASK( 4) |
12362306a36Sopenharmony_ci		      PKRU_AD_MASK( 5) | PKRU_AD_MASK( 6) |
12462306a36Sopenharmony_ci		      PKRU_AD_MASK( 7) | PKRU_AD_MASK( 8) |
12562306a36Sopenharmony_ci		      PKRU_AD_MASK( 9) | PKRU_AD_MASK(10) |
12662306a36Sopenharmony_ci		      PKRU_AD_MASK(11) | PKRU_AD_MASK(12) |
12762306a36Sopenharmony_ci		      PKRU_AD_MASK(13) | PKRU_AD_MASK(14) |
12862306a36Sopenharmony_ci		      PKRU_AD_MASK(15);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic ssize_t init_pkru_read_file(struct file *file, char __user *user_buf,
13162306a36Sopenharmony_ci			     size_t count, loff_t *ppos)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	char buf[32];
13462306a36Sopenharmony_ci	unsigned int len;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	len = sprintf(buf, "0x%x\n", init_pkru_value);
13762306a36Sopenharmony_ci	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic ssize_t init_pkru_write_file(struct file *file,
14162306a36Sopenharmony_ci		 const char __user *user_buf, size_t count, loff_t *ppos)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	char buf[32];
14462306a36Sopenharmony_ci	ssize_t len;
14562306a36Sopenharmony_ci	u32 new_init_pkru;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	len = min(count, sizeof(buf) - 1);
14862306a36Sopenharmony_ci	if (copy_from_user(buf, user_buf, len))
14962306a36Sopenharmony_ci		return -EFAULT;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* Make the buffer a valid string that we can not overrun */
15262306a36Sopenharmony_ci	buf[len] = '\0';
15362306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &new_init_pkru))
15462306a36Sopenharmony_ci		return -EINVAL;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * Don't allow insane settings that will blow the system
15862306a36Sopenharmony_ci	 * up immediately if someone attempts to disable access
15962306a36Sopenharmony_ci	 * or writes to pkey 0.
16062306a36Sopenharmony_ci	 */
16162306a36Sopenharmony_ci	if (new_init_pkru & (PKRU_AD_BIT|PKRU_WD_BIT))
16262306a36Sopenharmony_ci		return -EINVAL;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	WRITE_ONCE(init_pkru_value, new_init_pkru);
16562306a36Sopenharmony_ci	return count;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic const struct file_operations fops_init_pkru = {
16962306a36Sopenharmony_ci	.read = init_pkru_read_file,
17062306a36Sopenharmony_ci	.write = init_pkru_write_file,
17162306a36Sopenharmony_ci	.llseek = default_llseek,
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int __init create_init_pkru_value(void)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	/* Do not expose the file if pkeys are not supported. */
17762306a36Sopenharmony_ci	if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
17862306a36Sopenharmony_ci		return 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	debugfs_create_file("init_pkru", S_IRUSR | S_IWUSR,
18162306a36Sopenharmony_ci			arch_debugfs_dir, NULL, &fops_init_pkru);
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_cilate_initcall(create_init_pkru_value);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic __init int setup_init_pkru(char *opt)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	u32 new_init_pkru;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (kstrtouint(opt, 0, &new_init_pkru))
19162306a36Sopenharmony_ci		return 1;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	WRITE_ONCE(init_pkru_value, new_init_pkru);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 1;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci__setup("init_pkru=", setup_init_pkru);
198