Mark Wielaard 812b75
commit b064131bdf099d3647b4501e5d15391e1e9623e6
Mark Wielaard 812b75
Author: Mark Wielaard <mark@klomp.org>
Mark Wielaard 812b75
Date:   Thu May 30 00:29:58 2019 +0200
Mark Wielaard 812b75
Mark Wielaard 812b75
    linux x86 and amd64 memory protection key syscalls.
Mark Wielaard 812b75
    
Mark Wielaard 812b75
    This implements minimal support for the pkey_alloc, pkey_free and
Mark Wielaard 812b75
    pkey_mprotect syscalls. pkey_alloc will simply indicate that pkeys
Mark Wielaard 812b75
    are not supported. pkey_free always fails. pkey_mprotect works just
Mark Wielaard 812b75
    like mprotect if the special pkey -1 is provided.
Mark Wielaard 812b75
    
Mark Wielaard 812b75
    https://bugs.kde.org/show_bug.cgi?id=408091
Mark Wielaard 812b75
Mark Wielaard 812b75
diff --git a/coregrind/m_syswrap/priv_syswrap-generic.h b/coregrind/m_syswrap/priv_syswrap-generic.h
Mark Wielaard 812b75
index 88530f0..3e1c8b6 100644
Mark Wielaard 812b75
--- a/coregrind/m_syswrap/priv_syswrap-generic.h
Mark Wielaard 812b75
+++ b/coregrind/m_syswrap/priv_syswrap-generic.h
Mark Wielaard 812b75
@@ -106,6 +106,10 @@ extern Bool
Mark Wielaard 812b75
 ML_(handle_auxv_open)(SyscallStatus *status, const HChar *filename,
Mark Wielaard 812b75
                       int flags);
Mark Wielaard 812b75
 
Mark Wielaard 812b75
+/* Helper function for generic mprotect and linux pkey_mprotect. */
Mark Wielaard 812b75
+extern void handle_sys_mprotect (ThreadId tid, SyscallStatus *status,
Mark Wielaard 812b75
+                                 Addr *addr, SizeT *len, Int *prot);
Mark Wielaard 812b75
+
Mark Wielaard 812b75
 DECL_TEMPLATE(generic, sys_ni_syscall);            // * P -- unimplemented
Mark Wielaard 812b75
 DECL_TEMPLATE(generic, sys_exit);
Mark Wielaard 812b75
 DECL_TEMPLATE(generic, sys_fork);
Mark Wielaard 812b75
diff --git a/coregrind/m_syswrap/priv_syswrap-linux.h b/coregrind/m_syswrap/priv_syswrap-linux.h
Mark Wielaard 812b75
index 5cf5407..2471524 100644
Mark Wielaard 812b75
--- a/coregrind/m_syswrap/priv_syswrap-linux.h
Mark Wielaard 812b75
+++ b/coregrind/m_syswrap/priv_syswrap-linux.h
Mark Wielaard 812b75
@@ -299,6 +299,11 @@ DECL_TEMPLATE(linux, sys_bpf);
Mark Wielaard 812b75
 // Linux-specific (new in Linux 4.11)
Mark Wielaard 812b75
 DECL_TEMPLATE(linux, sys_statx);
Mark Wielaard 812b75
 
Mark Wielaard 812b75
+// Linux-specific memory protection key syscalls (since Linux 4.9)
Mark Wielaard 812b75
+DECL_TEMPLATE(linux, sys_pkey_alloc);
Mark Wielaard 812b75
+DECL_TEMPLATE(linux, sys_pkey_free);
Mark Wielaard 812b75
+DECL_TEMPLATE(linux, sys_pkey_mprotect);
Mark Wielaard 812b75
+
Mark Wielaard 812b75
 /* ---------------------------------------------------------------------
Mark Wielaard 812b75
    Wrappers for sockets and ipc-ery.  These are split into standalone
Mark Wielaard 812b75
    procedures because x86-linux hides them inside multiplexors
Mark Wielaard 812b75
diff --git a/coregrind/m_syswrap/syswrap-amd64-linux.c b/coregrind/m_syswrap/syswrap-amd64-linux.c
Mark Wielaard 812b75
index d4fe413..2d6b95f 100644
Mark Wielaard 812b75
--- a/coregrind/m_syswrap/syswrap-amd64-linux.c
Mark Wielaard 812b75
+++ b/coregrind/m_syswrap/syswrap-amd64-linux.c
Mark Wielaard 812b75
@@ -863,6 +863,10 @@ static SyscallTableEntry syscall_table[] = {
Mark Wielaard 812b75
    LINX_(__NR_membarrier,        sys_membarrier),        // 324
Mark Wielaard 812b75
 
Mark Wielaard 812b75
    LINX_(__NR_copy_file_range,   sys_copy_file_range),   // 326
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+   LINXY(__NR_pkey_mprotect,     sys_pkey_mprotect),     // 329
Mark Wielaard 812b75
+   LINX_(__NR_pkey_alloc,        sys_pkey_alloc),        // 330
Mark Wielaard 812b75
+   LINX_(__NR_pkey_free,         sys_pkey_free),         // 331
Mark Wielaard 812b75
 };
Mark Wielaard 812b75
 
Mark Wielaard 812b75
 SyscallTableEntry* ML_(get_linux_syscall_entry) ( UInt sysno )
Mark Wielaard 812b75
diff --git a/coregrind/m_syswrap/syswrap-generic.c b/coregrind/m_syswrap/syswrap-generic.c
Mark Wielaard 812b75
index 0b64919..01191f6 100644
Mark Wielaard 812b75
--- a/coregrind/m_syswrap/syswrap-generic.c
Mark Wielaard 812b75
+++ b/coregrind/m_syswrap/syswrap-generic.c
Mark Wielaard 812b75
@@ -3842,12 +3842,28 @@ PRE(sys_mprotect)
Mark Wielaard 812b75
    PRE_REG_READ3(long, "mprotect",
Mark Wielaard 812b75
                  unsigned long, addr, vki_size_t, len, unsigned long, prot);
Mark Wielaard 812b75
 
Mark Wielaard 812b75
-   if (!ML_(valid_client_addr)(ARG1, ARG2, tid, "mprotect")) {
Mark Wielaard 812b75
+   Addr addr = ARG1;
Mark Wielaard 812b75
+   SizeT len = ARG2;
Mark Wielaard 812b75
+   Int prot  = ARG3;
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+   handle_sys_mprotect (tid, status, &addr, &len, &prot;;
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+   ARG1 = addr;
Mark Wielaard 812b75
+   ARG2 = len;
Mark Wielaard 812b75
+   ARG3 = prot;
Mark Wielaard 812b75
+}
Mark Wielaard 812b75
+/* This will be called from the generic mprotect, or the linux specific
Mark Wielaard 812b75
+   pkey_mprotect. Pass pointers to ARG1, ARG2 and ARG3 as addr, len and prot,
Mark Wielaard 812b75
+   they might be adjusted and have to assigned back to ARG1, ARG2 and ARG3.  */
Mark Wielaard 812b75
+void handle_sys_mprotect(ThreadId tid, SyscallStatus* status,
Mark Wielaard 812b75
+                         Addr *addr, SizeT *len, Int *prot)
Mark Wielaard 812b75
+{
Mark Wielaard 812b75
+   if (!ML_(valid_client_addr)(*addr, *len, tid, "mprotect")) {
Mark Wielaard 812b75
       SET_STATUS_Failure( VKI_ENOMEM );
Mark Wielaard 812b75
    } 
Mark Wielaard 812b75
 #if defined(VKI_PROT_GROWSDOWN)
Mark Wielaard 812b75
    else 
Mark Wielaard 812b75
-   if (ARG3 & (VKI_PROT_GROWSDOWN|VKI_PROT_GROWSUP)) {
Mark Wielaard 812b75
+   if (*prot & (VKI_PROT_GROWSDOWN|VKI_PROT_GROWSUP)) {
Mark Wielaard 812b75
       /* Deal with mprotects on growable stack areas.
Mark Wielaard 812b75
 
Mark Wielaard 812b75
          The critical files to understand all this are mm/mprotect.c
Mark Wielaard 812b75
@@ -3862,8 +3878,8 @@ PRE(sys_mprotect)
Mark Wielaard 812b75
 
Mark Wielaard 812b75
          The sanity check provided by the kernel is that the vma must
Mark Wielaard 812b75
          have the VM_GROWSDOWN/VM_GROWSUP flag set as appropriate.  */
Mark Wielaard 812b75
-      UInt grows = ARG3 & (VKI_PROT_GROWSDOWN|VKI_PROT_GROWSUP);
Mark Wielaard 812b75
-      NSegment const *aseg = VG_(am_find_nsegment)(ARG1);
Mark Wielaard 812b75
+      UInt grows = *prot & (VKI_PROT_GROWSDOWN|VKI_PROT_GROWSUP);
Mark Wielaard 812b75
+      NSegment const *aseg = VG_(am_find_nsegment)(*addr);
Mark Wielaard 812b75
       NSegment const *rseg;
Mark Wielaard 812b75
 
Mark Wielaard 812b75
       vg_assert(aseg);
Mark Wielaard 812b75
@@ -3874,10 +3890,10 @@ PRE(sys_mprotect)
Mark Wielaard 812b75
              && rseg->kind == SkResvn
Mark Wielaard 812b75
              && rseg->smode == SmUpper
Mark Wielaard 812b75
              && rseg->end+1 == aseg->start) {
Mark Wielaard 812b75
-            Addr end = ARG1 + ARG2;
Mark Wielaard 812b75
-            ARG1 = aseg->start;
Mark Wielaard 812b75
-            ARG2 = end - aseg->start;
Mark Wielaard 812b75
-            ARG3 &= ~VKI_PROT_GROWSDOWN;
Mark Wielaard 812b75
+            Addr end = *addr + *len;
Mark Wielaard 812b75
+            *addr = aseg->start;
Mark Wielaard 812b75
+            *len = end - aseg->start;
Mark Wielaard 812b75
+            *prot &= ~VKI_PROT_GROWSDOWN;
Mark Wielaard 812b75
          } else {
Mark Wielaard 812b75
             SET_STATUS_Failure( VKI_EINVAL );
Mark Wielaard 812b75
          }
Mark Wielaard 812b75
@@ -3887,8 +3903,8 @@ PRE(sys_mprotect)
Mark Wielaard 812b75
              && rseg->kind == SkResvn
Mark Wielaard 812b75
              && rseg->smode == SmLower
Mark Wielaard 812b75
              && aseg->end+1 == rseg->start) {
Mark Wielaard 812b75
-            ARG2 = aseg->end - ARG1 + 1;
Mark Wielaard 812b75
-            ARG3 &= ~VKI_PROT_GROWSUP;
Mark Wielaard 812b75
+            *len = aseg->end - *addr + 1;
Mark Wielaard 812b75
+            *prot &= ~VKI_PROT_GROWSUP;
Mark Wielaard 812b75
          } else {
Mark Wielaard 812b75
             SET_STATUS_Failure( VKI_EINVAL );
Mark Wielaard 812b75
          }
Mark Wielaard 812b75
diff --git a/coregrind/m_syswrap/syswrap-linux.c b/coregrind/m_syswrap/syswrap-linux.c
Mark Wielaard 812b75
index 810ca24..5452b8d 100644
Mark Wielaard 812b75
--- a/coregrind/m_syswrap/syswrap-linux.c
Mark Wielaard 812b75
+++ b/coregrind/m_syswrap/syswrap-linux.c
Mark Wielaard 812b75
@@ -12120,6 +12120,76 @@ PRE(sys_copy_file_range)
Mark Wielaard 812b75
   }
Mark Wielaard 812b75
 }
Mark Wielaard 812b75
 
Mark Wielaard 812b75
+PRE(sys_pkey_alloc)
Mark Wielaard 812b75
+{
Mark Wielaard 812b75
+  PRINT("pkey_alloc (%lu, %lu)", ARG1, ARG2);
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+  PRE_REG_READ2(long, "pkey_alloc",
Mark Wielaard 812b75
+                unsigned long, "flags",
Mark Wielaard 812b75
+                unsigned long, "access_rights");
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+  /* The kernel says: pkey_alloc() is always safe to call regardless of
Mark Wielaard 812b75
+     whether or not the operating system supports protection keys.  It can be
Mark Wielaard 812b75
+     used in lieu of any other mechanism for detecting pkey support and will
Mark Wielaard 812b75
+     simply fail with the error ENOSPC if the operating system has no pkey
Mark Wielaard 812b75
+     support.
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+     So we simply always return ENOSPC to signal memory protection keys are
Mark Wielaard 812b75
+     not supported under valgrind, unless there are unknown flags, then we
Mark Wielaard 812b75
+     return EINVAL. */
Mark Wielaard 812b75
+  unsigned long pkey_flags = ARG1;
Mark Wielaard 812b75
+  if (pkey_flags != 0)
Mark Wielaard 812b75
+     SET_STATUS_Failure( VKI_EINVAL );
Mark Wielaard 812b75
+  else
Mark Wielaard 812b75
+     SET_STATUS_Failure( VKI_ENOSPC );
Mark Wielaard 812b75
+}
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+PRE(sys_pkey_free)
Mark Wielaard 812b75
+{
Mark Wielaard 812b75
+  PRINT("pkey_free (%" FMT_REGWORD "u )", ARG1);
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+  PRE_REG_READ1(long, "pkey_free",
Mark Wielaard 812b75
+                unsigned long, "pkey");
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+  /* Since pkey_alloc () can never succeed, see above, freeing any pkey is
Mark Wielaard 812b75
+     always an error.  */
Mark Wielaard 812b75
+  SET_STATUS_Failure( VKI_EINVAL );
Mark Wielaard 812b75
+}
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+PRE(sys_pkey_mprotect)
Mark Wielaard 812b75
+{
Mark Wielaard 812b75
+   PRINT("sys_pkey_mprotect ( %#" FMT_REGWORD "x, %" FMT_REGWORD "u, %"
Mark Wielaard 812b75
+         FMT_REGWORD "u %" FMT_REGWORD "u )", ARG1, ARG2, ARG3, ARG4);
Mark Wielaard 812b75
+   PRE_REG_READ4(long, "pkey_mprotect",
Mark Wielaard 812b75
+                 unsigned long, addr, vki_size_t, len, unsigned long, prot,
Mark Wielaard 812b75
+                 unsigned long, pkey);
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+   Addr  addr = ARG1;
Mark Wielaard 812b75
+   SizeT len  = ARG2;
Mark Wielaard 812b75
+   Int   prot = ARG3;
Mark Wielaard 812b75
+   Int   pkey = ARG4;
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+   /* Since pkey_alloc () can never succeed, see above, any pkey is
Mark Wielaard 812b75
+      invalid. Except for -1, then pkey_mprotect acts just like mprotect.  */
Mark Wielaard 812b75
+   if (pkey != -1)
Mark Wielaard 812b75
+      SET_STATUS_Failure( VKI_EINVAL );
Mark Wielaard 812b75
+   else
Mark Wielaard 812b75
+      handle_sys_mprotect (tid, status, &addr, &len, &prot;;
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+   ARG1 = addr;
Mark Wielaard 812b75
+   ARG2 = len;
Mark Wielaard 812b75
+   ARG3 = prot;
Mark Wielaard 812b75
+}
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+POST(sys_pkey_mprotect)
Mark Wielaard 812b75
+{
Mark Wielaard 812b75
+   Addr  addr = ARG1;
Mark Wielaard 812b75
+   SizeT len  = ARG2;
Mark Wielaard 812b75
+   Int   prot = ARG3;
Mark Wielaard 812b75
+
Mark Wielaard 812b75
+   ML_(notify_core_and_tool_of_mprotect)(addr, len, prot);
Mark Wielaard 812b75
+}
Mark Wielaard 812b75
+
Mark Wielaard 812b75
 
Mark Wielaard 812b75
 #undef PRE
Mark Wielaard 812b75
 #undef POST
Mark Wielaard 812b75
diff --git a/coregrind/m_syswrap/syswrap-x86-linux.c b/coregrind/m_syswrap/syswrap-x86-linux.c
Mark Wielaard 812b75
index ad54cf6..3829fa4 100644
Mark Wielaard 812b75
--- a/coregrind/m_syswrap/syswrap-x86-linux.c
Mark Wielaard 812b75
+++ b/coregrind/m_syswrap/syswrap-x86-linux.c
Mark Wielaard 812b75
@@ -1608,6 +1608,9 @@ static SyscallTableEntry syscall_table[] = {
Mark Wielaard 812b75
 
Mark Wielaard 812b75
    LINX_(__NR_copy_file_range,   sys_copy_file_range),   // 377
Mark Wielaard 812b75
 
Mark Wielaard 812b75
+   LINXY(__NR_pkey_mprotect,     sys_pkey_mprotect),    // 380
Mark Wielaard 812b75
+   LINX_(__NR_pkey_alloc,        sys_pkey_alloc),       // 381
Mark Wielaard 812b75
+   LINX_(__NR_pkey_free,         sys_pkey_free),        // 382
Mark Wielaard 812b75
    LINXY(__NR_statx,             sys_statx),            // 383
Mark Wielaard 812b75
 
Mark Wielaard 812b75
    /* Explicitly not supported on i386 yet. */