x86/pkeys: Allocation/free syscalls
[cascardo/linux.git] / mm / mprotect.c
index abd9c82..7b35ee3 100644 (file)
 #include <linux/mmu_notifier.h>
 #include <linux/migrate.h>
 #include <linux/perf_event.h>
+#include <linux/pkeys.h>
 #include <linux/ksm.h>
 #include <linux/pkeys.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/cacheflush.h>
+#include <asm/mmu_context.h>
 #include <asm/tlbflush.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 bool rier = (current->personality & READ_IMPLIES_EXEC) &&
                                (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);
        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(&current->mm->mmap_sem))
                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);
        error = -ENOMEM;
        if (!vma)
@@ -485,3 +489,48 @@ SYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len,
 {
        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(&current->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(&current->mm->mmap_sem);
+       return ret;
+}
+
+SYSCALL_DEFINE1(pkey_free, int, pkey)
+{
+       int ret;
+
+       down_write(&current->mm->mmap_sem);
+       ret = mm_pkey_free(current->mm, pkey);
+       up_write(&current->mm->mmap_sem);
+
+       /*
+        * We could provie warnings or errors if any VMA still
+        * has the pkey set here.
+        */
+       return ret;
+}