Jeroen Demeyer on Thu, 05 Jan 2017 10:48:00 +0100


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: Use PROT_NONE for unused virtual stack memory


Hello pari-dev,

In attachment you find an updated version of my patch, taking in account some comments by Bill Allombert and fixing #1881.

Cheers,
Jeroen.
>From 6c384e310aa12d59b4f02f13a8bddaad80a461ee Mon Sep 17 00:00:00 2001
From: Jeroen Demeyer <jdemeyer@cage.ugent.be>
Date: Tue, 22 Nov 2016 13:39:20 +0100
Subject: [PATCH] Use PROT_NONE for unused virtual stack memory

---
 config/has_mmap.c   |   7 +--
 src/language/init.c | 123 ++++++++++++++++++++++++++++++++++++++++++----------
 src/test/32/memory  |   8 ++++
 src/test/in/memory  |   2 +
 4 files changed, 111 insertions(+), 29 deletions(-)
 create mode 100644 src/test/32/memory
 create mode 100644 src/test/in/memory

diff --git a/config/has_mmap.c b/config/has_mmap.c
index 87d93cf..fa79053 100644
--- a/config/has_mmap.c
+++ b/config/has_mmap.c
@@ -3,15 +3,12 @@
 #ifndef MAP_ANONYMOUS
 #define MAP_ANONYMOUS MAP_ANON
 #endif
-#ifndef MAP_NORESERVE
-#define MAP_NORESERVE 0
-#endif
 int main(void)
 {
   size_t size = sysconf(_SC_PAGE_SIZE)*1000;
   void *b = mmap(NULL, size, PROT_READ|PROT_WRITE,
-                             MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0);
-  madvise(b, size, MADV_DONTNEED);
+                             MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
+  mmap(b, size, PROT_NONE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
   munmap(b, size);
   return 0;
 }
diff --git a/src/language/init.c b/src/language/init.c
index a079cdf..8f47e1c 100644
--- a/src/language/init.c
+++ b/src/language/init.c
@@ -622,14 +622,11 @@ pari_add_defaults_module(entree *ep)
 #ifndef MAP_ANONYMOUS
 #define MAP_ANONYMOUS MAP_ANON
 #endif
-#ifndef MAP_NORESERVE
-#define MAP_NORESERVE 0
-#endif
 static void *
 pari_mainstack_malloc(size_t size)
 {
   void *b = mmap(NULL, size, PROT_READ|PROT_WRITE,
-                             MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0);
+                             MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
   return (b == MAP_FAILED) ? NULL: b;
 }
 
@@ -639,10 +636,58 @@ pari_mainstack_mfree(void *s, size_t size)
   munmap(s, size);
 }
 
+/* Completely discard the memory mapped between the addresses "from"
+ * and "to" (which must be page-aligned).
+ *
+ * We use mmap() with PROT_NONE, which means that the underlying memory
+ * is freed and that the kernel should not commit memory for it. We
+ * still keep the mapping such that we can change the flags to
+ * PROT_READ|PROT_WRITE later.
+ *
+ * NOTE: remapping with MAP_FIXED and PROT_NONE is not the same as
+ * calling mprotect(..., PROT_NONE) because the latter will keep the
+ * memory committed (this is in particular relevant on Linux with
+ * vm.overcommit = 2). This remains true even when calling
+ * madvise(..., MADV_DONTNEED). */
 static void
-pari_mainstack_mreset(void *s, size_t size)
+pari_mainstack_mreset(pari_sp from, pari_sp to)
 {
-  madvise(s, size, MADV_DONTNEED);
+  size_t s = to - from;
+  mmap((void*)from, s, PROT_NONE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+}
+
+/* Commit (make available) the virtual memory mapped between the
+ * addresses "from" and "to" (which must be page-aligned).
+ * Return 0 if successful, -1 if failed. */
+static int
+pari_mainstack_mextend(pari_sp from, pari_sp to)
+{
+  size_t s = to - from;
+  return mprotect((void*)from, s, PROT_READ|PROT_WRITE);
+}
+
+/* Set actual stack size to the given size. This sets st->size and
+ * st->bot. If not enough system memory is available, this can fail.
+ * Return 1 if successful, 0 if failed (in that case, st->size is not
+ * changed) */
+static int
+pari_mainstack_setsize(struct pari_mainstack *st, size_t size)
+{
+  pari_sp newbot = st->top - size;
+  /* Align newbot to pagesize */
+  pari_sp alignbot = newbot & ~(pari_sp)(PARI_STACK_ALIGN - 1);
+  if (pari_mainstack_mextend(alignbot, st->top))
+  {
+    /* Making the memory available did not work: limit vsize to the
+     * current actual stack size. */
+    st->vsize = st->size;
+    pari_warn(warnstack, st->vsize);
+    return 0;
+  }
+  pari_mainstack_mreset(st->vbot, alignbot);
+  st->bot = newbot;
+  st->size = size;
+  return 1;
 }
 
 #else
@@ -657,7 +702,18 @@ static void
 pari_mainstack_mfree(void *s, size_t size) { (void) size; free(s); }
 
 static void
-pari_mainstack_mreset(void *s, size_t size) { (void) s; (void) size; }
+pari_mainstack_mreset(pari_sp from, pari_sp to) { (void) from; (void) to; }
+
+static int
+pari_mainstack_mextend(pari_sp from, pari_sp to) { (void) from; (void) to; return 0; }
+
+static int
+pari_mainstack_setsize(struct pari_mainstack *st, size_t size)
+{
+  st->bot = st->top - size;
+  st->size = size;
+  return 1;
+}
 
 #endif
 
@@ -686,9 +742,12 @@ pari_mainstack_alloc(struct pari_mainstack *st, size_t rsize, size_t vsize)
   }
   st->vsize = vsize ? s: 0;
   st->rsize = minuu(rsize, s);
-  st->size = st->rsize;
   st->top = st->vbot+s;
-  st->bot = st->top - st->size;
+  if (!pari_mainstack_setsize(st, st->rsize))
+  {
+    /* This should never happen since we only decrease the allocated space */
+    pari_err(e_MEM);
+  }
   st->memused = 0;
 }
 
@@ -697,7 +756,7 @@ pari_mainstack_free(struct pari_mainstack *st)
 {
   pari_mainstack_mfree((void*)st->vbot, st->vsize ? st->vsize : fix_size(st->rsize));
   st->top = st->bot = st->vbot = 0;
-  st->size = st->vsize =0;
+  st->size = st->vsize = 0;
 }
 
 static void
@@ -762,31 +821,47 @@ paristack_newrsize(ulong newsize)
 void
 paristack_resize(ulong newsize)
 {
-  size_t vsize = pari_mainstack->vsize;
   if (!newsize)
-    newsize = pari_mainstack->size << 1;
-  newsize = maxuu(minuu(newsize, vsize), pari_mainstack->size);
-  pari_mainstack->size = newsize;
-  pari_mainstack->bot = pari_mainstack->top - pari_mainstack->size;
-  pari_warn(warner,"increasing stack size to %lu",newsize);
+    newsize = 2 * pari_mainstack->size;
+  newsize = minuu(newsize, pari_mainstack->vsize);
+  if (newsize <= pari_mainstack->size) return;
+  if (pari_mainstack_setsize(pari_mainstack, newsize))
+  {
+    pari_warn(warner, "increasing stack size to %lu", pari_mainstack->size);
+  }
 }
 
 void
 parivstack_reset(void)
 {
-  pari_mainstack->size = pari_mainstack->rsize;
-  pari_mainstack->bot = pari_mainstack->top - pari_mainstack->size;
-  pari_mainstack_mreset((void *)pari_mainstack->vbot,
-                        pari_mainstack->bot-pari_mainstack->vbot);
+  pari_mainstack_setsize(pari_mainstack, pari_mainstack->rsize);
 }
 
+/* Enlarge the stack if needed such that the unused portion of the stack
+ * (between bot and avma) is large enough to contain x longs. */
 void
 new_chunk_resize(size_t x)
 {
-  if (pari_mainstack->vsize==0
-    || x > (avma-pari_mainstack->vbot) / sizeof(long)) pari_err(e_STACK);
-  while (x > (avma-pari_mainstack->bot) / sizeof(long))
-    paristack_resize(0);
+  ulong size, newsize, avail;
+  avail = (avma - pari_mainstack->bot) / sizeof(long);
+  if (avail >= x) return;
+
+  /* We need to enlarge the stack. We try to at least double the
+   * stack, to avoid increasing the stack a lot of times by a small
+   * amount. */
+  size = pari_mainstack->size;
+  newsize = size + maxuu((x - avail) * sizeof(long), size);
+  paristack_resize(newsize);
+
+  /* Verify that we have enough space. Using a division here instead
+   * of a multiplication is safe against overflow. */
+  avail = (avma - pari_mainstack->bot) / sizeof(long);
+  if (avail < x)
+  {
+    /* Restore old size and error out */
+    pari_mainstack_setsize(pari_mainstack, size);
+    pari_err(e_STACK);
+  }
 }
 
 /*********************************************************************/
diff --git a/src/test/32/memory b/src/test/32/memory
new file mode 100644
index 0000000..e865a17
--- /dev/null
+++ b/src/test/32/memory
@@ -0,0 +1,8 @@
+  ***   Warning: new stack size = 1048576 (1.000 Mbytes).
+  ***   at top-level: vector(100000,k,k)
+  ***                                 ^--
+  ***   the PARI stack overflows !
+  current stack size: 1048576 (1.000 Mbytes)
+  [hint] set 'parisizemax' to a non-zero value in your GPRC
+
+Total time spent: 10
diff --git a/src/test/in/memory b/src/test/in/memory
new file mode 100644
index 0000000..2a36a9b
--- /dev/null
+++ b/src/test/in/memory
@@ -0,0 +1,2 @@
+default(parisize, 2^20);
+vector(100000, k, k);        \\ #1881
-- 
2.7.3