From 2a1a877ffae57f74fef7f982df95d614ec9f2e82 Mon Sep 17 00:00:00 2001
From: Giuseppe Ottaviano <ott@fb.com>
Date: Sat, 19 Mar 2016 14:19:34 -0700
Subject: [PATCH] x86_64: fix mincore_validate

The detection logic introduced in 28f33c8ce0b654cf31d6beda9a612870662f3c56 is
broken, because it tests mincore using an address that is almost certainly not
page-aligned. straces confirms that msync is used all the time.

This patch fixes the logic by page-aligning the test pointer. strace now shows
that mincore is actually used. Furthermore, the return value of mincore is not
sufficient to assess whether the address can be safely dereferenced: we should
also check that the pages are mapped, through the passed mvec array. This patch
also adds this verification.

Tested on a system where unwinding a stack across a JNI boundary would cause
sporadic segfaults; no more crashes were observed after the patch.
---
 src/x86_64/Ginit.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c
index 6e9d4fe..7827576 100644
--- a/src/x86_64/Ginit.c
+++ b/src/x86_64/Ginit.c
@@ -30,6 +30,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 #include <config.h>
 #endif
 
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
@@ -81,7 +82,21 @@ static int msync_validate (void *addr, size_t len)
 static int mincore_validate (void *addr, size_t len)
 {
   unsigned char mvec[2]; /* Unaligned access may cross page boundary */
-  return mincore (addr, len, mvec);
+  size_t i;
+
+  /* mincore could fail with EAGAIN but we conservatively return -1
+     instead of looping. */
+  if (mincore (addr, len, mvec) != 0)
+    {
+      return -1;
+    }
+
+  for (i = 0; i < (len + PAGE_SIZE - 1) / PAGE_SIZE; i++)
+    {
+      if (!(mvec[i] & 1)) return -1;
+    }
+
+  return 0;
 }
 #endif
 
@@ -94,7 +109,12 @@ tdep_init_mem_validate (void)
 {
 #ifdef HAVE_MINCORE
   unsigned char present = 1;
-  if (mincore (&present, 1, &present) == 0)
+  unw_word_t addr = PAGE_START((unw_word_t)&present);
+  unsigned char mvec[1];
+  int ret;
+  while ((ret = mincore ((void*)addr, PAGE_SIZE, mvec)) == -1 &&
+         errno == EAGAIN) {}
+  if (ret == 0 && (mvec[0] & 1))
     {
       Debug(1, "using mincore to validate memory\n");
       mem_validate_func = mincore_validate;
-- 
2.8.0-rc2

