|
@@ -23,11 +23,13 @@
|
|
#include <linux/mmu_notifier.h>
|
|
#include <linux/mmu_notifier.h>
|
|
#include <linux/migrate.h>
|
|
#include <linux/migrate.h>
|
|
#include <linux/perf_event.h>
|
|
#include <linux/perf_event.h>
|
|
|
|
+#include <linux/pkeys.h>
|
|
#include <linux/ksm.h>
|
|
#include <linux/ksm.h>
|
|
#include <linux/pkeys.h>
|
|
#include <linux/pkeys.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/cacheflush.h>
|
|
|
|
+#include <asm/mmu_context.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/tlbflush.h>
|
|
|
|
|
|
#include "internal.h"
|
|
#include "internal.h"
|
|
@@ -364,12 +366,6 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
|
|
const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
|
|
const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
|
|
const bool rier = (current->personality & READ_IMPLIES_EXEC) &&
|
|
const bool rier = (current->personality & READ_IMPLIES_EXEC) &&
|
|
(prot & PROT_READ);
|
|
(prot & PROT_READ);
|
|
- /*
|
|
|
|
- * A temporary safety check since we are not validating
|
|
|
|
- * the pkey before we introduce the allocation code.
|
|
|
|
- */
|
|
|
|
- if (pkey != -1)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
|
|
prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP);
|
|
prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP);
|
|
if (grows == (PROT_GROWSDOWN|PROT_GROWSUP)) /* can't be both */
|
|
if (grows == (PROT_GROWSDOWN|PROT_GROWSUP)) /* can't be both */
|
|
@@ -391,6 +387,14 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
|
|
if (down_write_killable(¤t->mm->mmap_sem))
|
|
if (down_write_killable(¤t->mm->mmap_sem))
|
|
return -EINTR;
|
|
return -EINTR;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If userspace did not allocate the pkey, do not let
|
|
|
|
+ * them use it here.
|
|
|
|
+ */
|
|
|
|
+ error = -EINVAL;
|
|
|
|
+ if ((pkey != -1) && !mm_pkey_is_allocated(current->mm, pkey))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
vma = find_vma(current->mm, start);
|
|
vma = find_vma(current->mm, start);
|
|
error = -ENOMEM;
|
|
error = -ENOMEM;
|
|
if (!vma)
|
|
if (!vma)
|
|
@@ -485,3 +489,48 @@ SYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len,
|
|
{
|
|
{
|
|
return do_mprotect_pkey(start, len, prot, pkey);
|
|
return do_mprotect_pkey(start, len, prot, pkey);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+SYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val)
|
|
|
|
+{
|
|
|
|
+ int pkey;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* No flags supported yet. */
|
|
|
|
+ if (flags)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ /* check for unsupported init values */
|
|
|
|
+ if (init_val & ~PKEY_ACCESS_MASK)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ down_write(¤t->mm->mmap_sem);
|
|
|
|
+ pkey = mm_pkey_alloc(current->mm);
|
|
|
|
+
|
|
|
|
+ ret = -ENOSPC;
|
|
|
|
+ if (pkey == -1)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ret = arch_set_user_pkey_access(current, pkey, init_val);
|
|
|
|
+ if (ret) {
|
|
|
|
+ mm_pkey_free(current->mm, pkey);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ ret = pkey;
|
|
|
|
+out:
|
|
|
|
+ up_write(¤t->mm->mmap_sem);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+SYSCALL_DEFINE1(pkey_free, int, pkey)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ down_write(¤t->mm->mmap_sem);
|
|
|
|
+ ret = mm_pkey_free(current->mm, pkey);
|
|
|
|
+ up_write(¤t->mm->mmap_sem);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We could provie warnings or errors if any VMA still
|
|
|
|
+ * has the pkey set here.
|
|
|
|
+ */
|
|
|
|
+ return ret;
|
|
|
|
+}
|