Fix an assertion in the address space manager. BZ #345887.
The VG_(extend_stack) call needs to be properly guarded because the
passed-in address is not necessarily part of an extensible stack
segment. And an extensible stack segment is the only thing that
function should have to deal with.
Previously, the function VG_(am_addr_is_in_extensible_client_stack)
was introduced to guard VG_(extend_stack) but it was not added in all
places it should have been.

Also, extending the client stack during signal delivery (in sigframe-common.c)
was simply calling VG_(extend_stack) hoping it would do the right thing.
But that was not always the case. The new testcase 
none/tests/linux/pthread-stack.c exercises this (3.10.1 errors out on it).

Renamed ML_(sf_extend_stack) to ML_(sf_maybe_extend_stack) and add
proper guard logic for VG_(extend_stack).

Testcases none/tests/{amd64|x86}-linux/bug345887.c by Ivo Raisr.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15138 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index 9fdc795..2a61168 100644
--- a/NEWS
+++ b/NEWS
@@ -151,6 +151,7 @@
 345016  helgrind/tests/locked_vs_unlocked2 is failing sometimes
 345394  Fix memcheck/tests/strchr on OS X
 345637  Fix memcheck/tests/sendmsg on OS X
+345887  Fix an assertion in the address space manager
 346307  fuse filesystem syscall deadlocks
 n-i-bz  Provide implementations of certain compiler builtins to support
         compilers who may not provide those
diff --git a/configure.ac b/configure.ac
index a5aa964..d03aa5c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3033,6 +3033,7 @@
    none/tests/tilegx/Makefile
    none/tests/linux/Makefile
    none/tests/darwin/Makefile
+   none/tests/amd64-linux/Makefile
    none/tests/x86-linux/Makefile
    exp-sgcheck/Makefile
    exp-sgcheck/tests/Makefile
diff --git a/coregrind/m_sigframe/priv_sigframe.h b/coregrind/m_sigframe/priv_sigframe.h
index a81f4d0..7435185 100644
--- a/coregrind/m_sigframe/priv_sigframe.h
+++ b/coregrind/m_sigframe/priv_sigframe.h
@@ -37,7 +37,8 @@
 
 /* --------------- Implemented in sigframe-common.c ---------------*/
 
-Bool ML_(sf_extend_stack)( const ThreadState *tst, Addr addr, SizeT size );
+Bool ML_(sf_maybe_extend_stack)( const ThreadState *tst, Addr addr,
+                                 SizeT size, UInt flags );
 
 #endif   // __PRIV_SIGFRAME_H
 
diff --git a/coregrind/m_sigframe/sigframe-amd64-darwin.c b/coregrind/m_sigframe/sigframe-amd64-darwin.c
index 77752be..38682f3 100644
--- a/coregrind/m_sigframe/sigframe-amd64-darwin.c
+++ b/coregrind/m_sigframe/sigframe-amd64-darwin.c
@@ -109,7 +109,7 @@
                 entry to a function. */
 
    tst = VG_(get_ThreadState)(tid);
-   if (! ML_(sf_extend_stack)(tst, rsp, sp_top_of_frame - rsp))
+   if (! ML_(sf_maybe_extend_stack)(tst, rsp, sp_top_of_frame - rsp, flags))
       return;
 
    vg_assert(VG_IS_16_ALIGNED(rsp+8));
diff --git a/coregrind/m_sigframe/sigframe-amd64-linux.c b/coregrind/m_sigframe/sigframe-amd64-linux.c
index a2b79e5..e015401 100644
--- a/coregrind/m_sigframe/sigframe-amd64-linux.c
+++ b/coregrind/m_sigframe/sigframe-amd64-linux.c
@@ -412,7 +412,7 @@
    rsp = VG_ROUNDDN(rsp, 16) - 8;
    frame = (struct rt_sigframe *)rsp;
 
-   if (! ML_(sf_extend_stack)(tst, rsp, sizeof(*frame)))
+   if (! ML_(sf_maybe_extend_stack)(tst, rsp, sizeof(*frame), flags))
       return rsp_top_of_frame;
 
    /* retaddr, siginfo, uContext fields are to be written */
diff --git a/coregrind/m_sigframe/sigframe-arm-linux.c b/coregrind/m_sigframe/sigframe-arm-linux.c
index dbbebc3..7ae7f07 100644
--- a/coregrind/m_sigframe/sigframe-arm-linux.c
+++ b/coregrind/m_sigframe/sigframe-arm-linux.c
@@ -185,7 +185,7 @@
    sp -= size;
    sp = VG_ROUNDDN(sp, 16);
 
-   if(! ML_(sf_extend_stack)(tst, sp, size))
+   if (! ML_(sf_maybe_extend_stack)(tst, sp, size, flags))
       I_die_here; // XXX Incorrect behavior
 
 
diff --git a/coregrind/m_sigframe/sigframe-arm64-linux.c b/coregrind/m_sigframe/sigframe-arm64-linux.c
index 1b09696..1a7c984 100644
--- a/coregrind/m_sigframe/sigframe-arm64-linux.c
+++ b/coregrind/m_sigframe/sigframe-arm64-linux.c
@@ -173,7 +173,7 @@
    sp -= size;
    sp = VG_ROUNDDN(sp, 16);
 
-   if (! ML_(sf_extend_stack)(tst, sp, size))
+   if (! ML_(sf_maybe_extend_stack)(tst, sp, size, flags))
       return; // Give up.  No idea if this is correct
 
    struct rt_sigframe *rsf = (struct rt_sigframe *)sp;
diff --git a/coregrind/m_sigframe/sigframe-common.c b/coregrind/m_sigframe/sigframe-common.c
index 59b34ee..12ef1ed 100644
--- a/coregrind/m_sigframe/sigframe-common.c
+++ b/coregrind/m_sigframe/sigframe-common.c
@@ -54,16 +54,31 @@
 /* Extend the stack segment downwards if needed so as to ensure the
    new signal frames are mapped to something.  Return a Bool
    indicating whether or not the operation was successful. */
-Bool ML_(sf_extend_stack) ( const ThreadState *tst, Addr addr, SizeT size )
+Bool ML_(sf_maybe_extend_stack) ( const ThreadState *tst, Addr addr,
+                                  SizeT size, UInt flags )
 {
    ThreadId        tid = tst->tid;
    const NSegment *stackseg = NULL;
 
-   if (VG_(extend_stack)(tid, addr)) {
+   if (flags & VKI_SA_ONSTACK) {
+      /* If the sigframe is allocated on an alternate stack, then we cannot
+         extend that stack. Nothing to do here. */
       stackseg = VG_(am_find_nsegment)(addr);
-      if (0 && stackseg)
-	 VG_(printf)("frame=%#lx seg=%#lx-%#lx\n",
-		     addr, stackseg->start, stackseg->end);
+   } else if (VG_(am_addr_is_in_extensible_client_stack)(addr)) {
+      if (VG_(extend_stack)(tid, addr)) {
+         stackseg = VG_(am_find_nsegment)(addr);
+         if (0 && stackseg)
+            VG_(printf)("frame=%#lx seg=%#lx-%#lx\n",
+                        addr, stackseg->start, stackseg->end);
+      }
+   } else if ((stackseg = VG_(am_find_nsegment)(addr)) &&
+              VG_(am_is_valid_for_client)(addr, 1,
+                                          VKI_PROT_READ | VKI_PROT_WRITE)) {
+      /* We come here for explicitly defined pthread-stacks which can be
+         located in any client segment. */
+   } else {
+      /* Something unexpected */
+      stackseg = NULL;
    }
 
    if (stackseg == NULL || !stackseg->hasR || !stackseg->hasW) {
diff --git a/coregrind/m_sigframe/sigframe-mips32-linux.c b/coregrind/m_sigframe/sigframe-mips32-linux.c
index 6f09e4d..0e27e01 100644
--- a/coregrind/m_sigframe/sigframe-mips32-linux.c
+++ b/coregrind/m_sigframe/sigframe-mips32-linux.c
@@ -148,7 +148,7 @@
     }
 
   tst = VG_(get_ThreadState)(tid);
-  if (! ML_(sf_extend_stack)(tst, sp, sp_top_of_frame - sp))
+  if (! ML_(sf_maybe_extend_stack)(tst, sp, sp_top_of_frame - sp, flags))
     return;
 
   vg_assert(VG_IS_8_ALIGNED(sp));
diff --git a/coregrind/m_sigframe/sigframe-mips64-linux.c b/coregrind/m_sigframe/sigframe-mips64-linux.c
index fa6615b..cacf9ce 100644
--- a/coregrind/m_sigframe/sigframe-mips64-linux.c
+++ b/coregrind/m_sigframe/sigframe-mips64-linux.c
@@ -135,7 +135,7 @@
    sp = sp_top_of_frame - sizeof(struct rt_sigframe);
 
    tst = VG_(get_ThreadState)(tid);
-   if (! ML_(sf_extend_stack)(tst, sp, sp_top_of_frame - sp))
+   if (! ML_(sf_maybe_extend_stack)(tst, sp, sp_top_of_frame - sp, flags))
       return;
 
    sp = VG_ROUNDDN(sp, 16);
diff --git a/coregrind/m_sigframe/sigframe-ppc32-linux.c b/coregrind/m_sigframe/sigframe-ppc32-linux.c
index 346abb2..467814d 100644
--- a/coregrind/m_sigframe/sigframe-ppc32-linux.c
+++ b/coregrind/m_sigframe/sigframe-ppc32-linux.c
@@ -650,7 +650,7 @@
 
    tst = VG_(get_ThreadState)(tid);
 
-   if (! ML_(sf_extend_stack)(tst, sp, sp_top_of_frame - sp))
+   if (! ML_(sf_maybe_extend_stack)(tst, sp, sp_top_of_frame - sp, flags))
       return;
 
    vg_assert(VG_IS_16_ALIGNED(sp));
diff --git a/coregrind/m_sigframe/sigframe-ppc64-linux.c b/coregrind/m_sigframe/sigframe-ppc64-linux.c
index 8a6bcb3..5e90e54 100644
--- a/coregrind/m_sigframe/sigframe-ppc64-linux.c
+++ b/coregrind/m_sigframe/sigframe-ppc64-linux.c
@@ -158,7 +158,7 @@
    sp = sp_top_of_frame - sizeof(struct rt_sigframe);
 
    tst = VG_(get_ThreadState)(tid);
-   if (! ML_(sf_extend_stack)(tst, sp, sp_top_of_frame - sp))
+   if (! ML_(sf_maybe_extend_stack)(tst, sp, sp_top_of_frame - sp, flags))
       return;
 
    vg_assert(VG_IS_16_ALIGNED(sp));
diff --git a/coregrind/m_sigframe/sigframe-s390x-linux.c b/coregrind/m_sigframe/sigframe-s390x-linux.c
index b3ca4ae..0568922 100644
--- a/coregrind/m_sigframe/sigframe-s390x-linux.c
+++ b/coregrind/m_sigframe/sigframe-s390x-linux.c
@@ -298,7 +298,7 @@
    sp -= sizeof(*frame);
    frame = (struct sigframe *)sp;
 
-   if (! ML_(sf_extend_stack)(tst, sp, sizeof(*frame)))
+   if (! ML_(sf_maybe_extend_stack)(tst, sp, sizeof(*frame), flags))
       return sp_top_of_frame;
 
    /* retcode, sigNo, sc, sregs fields are to be written */
@@ -358,7 +358,7 @@
    sp -= sizeof(*frame);
    frame = (struct rt_sigframe *)sp;
 
-   if (! ML_(sf_extend_stack)(tst, sp, sizeof(*frame)))
+   if (! ML_(sf_maybe_extend_stack)(tst, sp, sizeof(*frame), flags))
       return sp_top_of_frame;
 
    /* retcode, sigNo, sc, sregs fields are to be written */
diff --git a/coregrind/m_sigframe/sigframe-tilegx-linux.c b/coregrind/m_sigframe/sigframe-tilegx-linux.c
index 48d0859..b9de920 100644
--- a/coregrind/m_sigframe/sigframe-tilegx-linux.c
+++ b/coregrind/m_sigframe/sigframe-tilegx-linux.c
@@ -158,7 +158,7 @@
   sp = sp_top_of_frame - sizeof(struct rt_sigframe);
 
   tst = VG_(get_ThreadState)(tid);
-  if (! ML_(sf_extend_stack)(tst, sp, sizeof(struct rt_sigframe)))
+  if (! ML_(sf_maybe_extend_stack)(tst, sp, sizeof(struct rt_sigframe), flags))
     return;
 
   vg_assert(VG_IS_8_ALIGNED(sp));
diff --git a/coregrind/m_sigframe/sigframe-x86-darwin.c b/coregrind/m_sigframe/sigframe-x86-darwin.c
index 6c2c1ef..e3da65d 100644
--- a/coregrind/m_sigframe/sigframe-x86-darwin.c
+++ b/coregrind/m_sigframe/sigframe-x86-darwin.c
@@ -112,7 +112,7 @@
                 entry to a function. */
 
    tst = VG_(get_ThreadState)(tid);
-   if (! ML_(sf_extend_stack)(tst, esp, sp_top_of_frame - esp))
+   if (! ML_(sf_maybe_extend_stack)(tst, esp, sp_top_of_frame - esp, flags))
       return;
 
    vg_assert(VG_IS_16_ALIGNED(esp+4));
diff --git a/coregrind/m_sigframe/sigframe-x86-linux.c b/coregrind/m_sigframe/sigframe-x86-linux.c
index d6597b6..a09aaf2 100644
--- a/coregrind/m_sigframe/sigframe-x86-linux.c
+++ b/coregrind/m_sigframe/sigframe-x86-linux.c
@@ -434,7 +434,7 @@
    esp = VG_ROUNDDN(esp, 16);
    frame = (struct sigframe *)esp;
 
-   if (! ML_(sf_extend_stack)(tst, esp, sizeof(*frame)))
+   if (! ML_(sf_maybe_extend_stack)(tst, esp, sizeof(*frame), flags))
       return esp_top_of_frame;
 
    /* retaddr, sigNo, siguContext fields are to be written */
@@ -491,7 +491,7 @@
    esp = VG_ROUNDDN(esp, 16);
    frame = (struct rt_sigframe *)esp;
 
-   if (! ML_(sf_extend_stack)(tst, esp, sizeof(*frame)))
+   if (! ML_(sf_maybe_extend_stack)(tst, esp, sizeof(*frame), flags))
       return esp_top_of_frame;
 
    /* retaddr, sigNo, pSiginfo, puContext fields are to be written */
diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c
index 5dc5f2f..47a345f 100644
--- a/coregrind/m_signals.c
+++ b/coregrind/m_signals.c
@@ -1737,7 +1737,8 @@
          if (tid == 1) {           // main thread
             Addr esp  = VG_(get_SP)(tid);
             Addr base = VG_PGROUNDDN(esp - VG_STACK_REDZONE_SZB);
-            if (VG_(extend_stack)(tid, base)) {
+            if (VG_(am_addr_is_in_extensible_client_stack)(base) &&
+                VG_(extend_stack)(tid, base)) {
                if (VG_(clo_trace_signals))
                   VG_(dmsg)("       -> extended stack base to %#lx\n",
                             VG_PGROUNDDN(esp));
@@ -2462,7 +2463,8 @@
          then extend the stack segment. 
        */
       Addr base = VG_PGROUNDDN(esp - VG_STACK_REDZONE_SZB);
-      if (VG_(extend_stack)(tid, base)) {
+      if (VG_(am_addr_is_in_extensible_client_stack)(base) &&
+          VG_(extend_stack)(tid, base)) {
          if (VG_(clo_trace_signals))
             VG_(dmsg)("       -> extended stack base to %#lx\n",
                       VG_PGROUNDDN(fault));
diff --git a/none/tests/Makefile.am b/none/tests/Makefile.am
index 8997064..4edbeaa 100644
--- a/none/tests/Makefile.am
+++ b/none/tests/Makefile.am
@@ -45,12 +45,15 @@
 endif
 
 # Platform-specific tests
+if VGCONF_PLATFORMS_INCLUDE_AMD64_LINUX
+SUBDIRS += amd64-linux
+endif
 if VGCONF_PLATFORMS_INCLUDE_X86_LINUX
 SUBDIRS += x86-linux
 endif
 
 DIST_SUBDIRS = x86 amd64 ppc32 ppc64 arm arm64 s390x mips32 mips64 tilegx \
-               linux darwin x86-linux scripts .
+               linux darwin amd64-linux x86-linux scripts .
 
 dist_noinst_SCRIPTS = \
 	filter_cmdline0 \
diff --git a/none/tests/amd64-linux/Makefile.am b/none/tests/amd64-linux/Makefile.am
new file mode 100644
index 0000000..39d7bac
--- /dev/null
+++ b/none/tests/amd64-linux/Makefile.am
@@ -0,0 +1,15 @@
+
+include $(top_srcdir)/Makefile.tool-tests.am
+
+dist_noinst_SCRIPTS = \
+	filter_stderr filter_minimal
+
+EXTRA_DIST = \
+	bug345887.stderr.exp bug345887.vgtest
+
+check_PROGRAMS = \
+	bug345887
+
+AM_CFLAGS    += @FLAG_M64@
+AM_CXXFLAGS  += @FLAG_M64@
+AM_CCASFLAGS += @FLAG_M64@
diff --git a/none/tests/amd64-linux/bug345887.c b/none/tests/amd64-linux/bug345887.c
new file mode 100644
index 0000000..0f9237d
--- /dev/null
+++ b/none/tests/amd64-linux/bug345887.c
@@ -0,0 +1,42 @@
+/* This test used to cause an assertion in the address space manager */
+
+__attribute__((noinline))
+static void inner(void)
+{
+   /* Set registers to apriori known values. */
+   __asm__ __volatile__(
+      "movq $0x101, %%rax\n"
+      "movq $0x102, %%rbx\n"
+      "movq $0x103, %%rcx\n"
+      "movq $0x104, %%rdx\n"
+      "movq $0x105, %%rsi\n"
+      "movq $0x106, %%rdi\n"
+      "movq $0x107, %%r8\n"
+      "movq $0x108, %%r9\n"
+      "movq $0x109, %%r10\n"
+      "movq $0x10a, %%r11\n"
+      "movq $0x10b, %%r12\n"
+      "movq $0x10c, %%r13\n"
+      "movq $0x10d, %%r14\n"
+      "movq $0x10e, %%r15\n"
+      // not %rbp as mdb is then not able to reconstruct stack trace
+      "movq $0x10f, %%rsp\n"
+      "movq $0x1234, (%%rax)\n"  // should cause SEGV here
+      "ud2"                      // should never get here
+      : // no output registers
+      : // no input registers
+      : "memory", "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi",
+        "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "%rsp");
+}
+
+__attribute__((noinline))
+static void outer(void)
+{
+   inner();
+}
+
+int main(int argc, const char *argv[])
+{
+   outer();
+   return 0;
+}
diff --git a/none/tests/amd64-linux/bug345887.stderr.exp b/none/tests/amd64-linux/bug345887.stderr.exp
new file mode 100644
index 0000000..129193c
--- /dev/null
+++ b/none/tests/amd64-linux/bug345887.stderr.exp
@@ -0,0 +1,9 @@
+
+Process terminating with default action of signal 11 (SIGSEGV)
+ Access not within mapped region at address 0x........
+   at 0x........: inner (bug345887.c:7)
+ If you believe this happened as a result of a stack
+ overflow in your program's main thread (unlikely but
+ possible), you can try to increase the size of the
+ main thread stack using the --main-stacksize= flag.
+ The main thread stack size used in this run was ....
diff --git a/none/tests/amd64-linux/bug345887.vgtest b/none/tests/amd64-linux/bug345887.vgtest
new file mode 100644
index 0000000..c014423
--- /dev/null
+++ b/none/tests/amd64-linux/bug345887.vgtest
@@ -0,0 +1,4 @@
+prog: bug345887
+vgopts: -q
+stderr_filter: filter_minimal
+cleanup: rm -f vgcore.*
diff --git a/none/tests/amd64-linux/filter_minimal b/none/tests/amd64-linux/filter_minimal
new file mode 100755
index 0000000..e69398c
--- /dev/null
+++ b/none/tests/amd64-linux/filter_minimal
@@ -0,0 +1,20 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+# Remove ==pid== and **pid** strings 
+perl -p -e 's/(==|\*\*)[0-9]{1,7}\1 //' |
+
+perl -p -e 's/0x[0-9A-Fa-f]+/0x......../g' |
+
+# Older bash versions print abnormal termination messages on the stderr
+# of the bash process. Newer bash versions redirect such messages properly.
+# Suppress any redirected abnormal termination messages. You can find the
+# complete list of messages in the bash source file siglist.c.
+perl -n -e 'print if !/^(Segmentation fault|Alarm clock|Aborted|Bus error)( \(core dumped\))?$/' |
+
+# Remove the size in "The main thread stack size..." message.
+sed "s/The main thread stack size used in this run was [0-9]*/The main thread stack size used in this run was .../"
+
+# NOTE: it is essential for the bug345887 testcase that the stderr
+#       filtering does *not* remove lines beginning with --
diff --git a/none/tests/amd64-linux/filter_stderr b/none/tests/amd64-linux/filter_stderr
new file mode 100755
index 0000000..587754a
--- /dev/null
+++ b/none/tests/amd64-linux/filter_stderr
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+# Remove ==pid== and --pid-- and **pid** strings 
+perl -p -e 's/(==|--|\*\*)[0-9]{1,7}\1 //' |
+
+perl -p -e 's/0x[0-9A-Fa-f]+/0x......../g'
+
+# NOTE: it is essential for the bug345887 testcase that the stderr
+#       filtering does *not* remove lines beginning with --
diff --git a/none/tests/linux/Makefile.am b/none/tests/linux/Makefile.am
index 87f780c..0ac75da 100644
--- a/none/tests/linux/Makefile.am
+++ b/none/tests/linux/Makefile.am
@@ -9,6 +9,7 @@
 	    mremap.vgtest \
 	mremap2.stderr.exp mremap2.stdout.exp mremap2.vgtest \
 	mremap3.stderr.exp mremap3.stdout.exp mremap3.vgtest \
+	pthread-stack.stderr.exp pthread-stack.vgtest \
 	stack-overflow.stderr.exp stack-overflow.vgtest
 
 check_PROGRAMS = \
@@ -16,8 +17,12 @@
 	mremap \
 	mremap2 \
 	mremap3 \
+	pthread-stack \
 	stack-overflow
 
 
 AM_CFLAGS   += $(AM_FLAG_M3264_PRI)
 AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
+
+# Special needs
+pthread_stack_LDADD = -lpthread
diff --git a/none/tests/linux/pthread-stack.c b/none/tests/linux/pthread-stack.c
new file mode 100644
index 0000000..4b7a953
--- /dev/null
+++ b/none/tests/linux/pthread-stack.c
@@ -0,0 +1,109 @@
+/* This test causes an error in 3.10.1 and earlier versions like so:
+
+==8336== Can't extend stack to 0x4033f98 during signal delivery for thread 2:
+==8336==   no stack segment
+
+  The reason was that only AnonC segments were considered as stack
+  segments. */
+ 
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+static volatile char *lowest_j;
+static jmp_buf goback;
+
+static void sigsegv_handler(int signr)
+{
+   longjmp(goback, 1);
+}
+
+static void bad_things_till_guard_page(void)
+{
+   fprintf(stderr, "... doing bad things till guard page\n");
+   char j = 0;
+   char *p = &j;
+
+   for (;;) {
+      j = j + *p;
+      p = p - 400;
+      lowest_j = p;
+   }
+}
+
+static void say_something(void)
+{
+  fprintf(stderr, "plugh\n");
+}
+
+static void* child_func ( void* arg )
+{
+   if (setjmp(goback)) {
+      say_something();
+   } else
+      bad_things_till_guard_page();
+
+   return NULL;
+}
+
+int main(int argc, const char** argv)
+{
+   int r, fd;
+
+   /* We will discover the thread guard page using SEGV.
+      So, prepare an handler. */
+   struct sigaction sa;
+   sa.sa_handler = sigsegv_handler;
+   sigemptyset(&sa.sa_mask);
+   sa.sa_flags = 0;
+   if (sigaction (SIGSEGV, &sa, NULL) != 0)
+      perror("sigaction");
+
+   pthread_t child;
+
+   /* Create a file that will be used as stack for a pthread.  */
+   const size_t file_size = 1024 * 1024;
+   const char file_name[] = "FILE";
+   fd = open(file_name, O_CREAT|O_WRONLY, 
+             S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+   assert(fd > 0);
+   void *p = malloc(file_size);
+   assert(p != 0);
+   memset(p, 0, file_size);
+   int written = write(fd, p, file_size);
+   assert(written == file_size);
+   close(fd);
+
+   /* Create a file-based stack for the child */
+   fd = open(file_name, O_CREAT|O_RDWR, 
+             S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+   assert(fd > 0);
+   const size_t stack_size = 256 * 1024;
+   assert(stack_size < file_size);
+   void *stack = mmap(NULL, stack_size, PROT_READ|PROT_WRITE, 
+                      MAP_PRIVATE, fd, 0);
+   assert(stack != (void *)-1);
+   pthread_attr_t attr;
+   pthread_attr_init(&attr);
+   r = pthread_attr_setstack(&attr, stack, stack_size);
+   assert(r == 0);
+   
+   /* Create child run */
+   r = pthread_create(&child, &attr, child_func, NULL);
+   assert(r == 0);
+   r = pthread_join(child, NULL);
+   assert(r == 0);
+
+   /* Remove file */
+   unlink(file_name);
+   return 0;
+}
+
diff --git a/none/tests/linux/pthread-stack.stderr.exp b/none/tests/linux/pthread-stack.stderr.exp
new file mode 100644
index 0000000..391e60c
--- /dev/null
+++ b/none/tests/linux/pthread-stack.stderr.exp
@@ -0,0 +1,2 @@
+... doing bad things till guard page
+plugh
diff --git a/none/tests/linux/pthread-stack.vgtest b/none/tests/linux/pthread-stack.vgtest
new file mode 100644
index 0000000..50cafcb
--- /dev/null
+++ b/none/tests/linux/pthread-stack.vgtest
@@ -0,0 +1,2 @@
+prog: pthread-stack
+vgopts: -q
diff --git a/none/tests/x86-linux/Makefile.am b/none/tests/x86-linux/Makefile.am
index 8592f1d..4fa0fac 100644
--- a/none/tests/x86-linux/Makefile.am
+++ b/none/tests/x86-linux/Makefile.am
@@ -2,14 +2,16 @@
 include $(top_srcdir)/Makefile.tool-tests.am
 
 dist_noinst_SCRIPTS = \
-	filter_stderr
+	filter_stderr filter_minimal
 
 EXTRA_DIST = \
+	bug345887.stderr.exp bug345887.vgtest \
 	hang.stderr.exp hang.vgtest \
 	seg_override.stderr.exp seg_override.stdout.exp seg_override.vgtest \
 	sigcontext.stdout.exp sigcontext.stderr.exp sigcontext.vgtest
 
 check_PROGRAMS = \
+	bug345887 \
 	hang \
 	seg_override \
 	sigcontext
@@ -17,5 +19,3 @@
 AM_CFLAGS    += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE)
 AM_CXXFLAGS  += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE)
 AM_CCASFLAGS += @FLAG_M32@
-
-
diff --git a/none/tests/x86-linux/bug345887.c b/none/tests/x86-linux/bug345887.c
new file mode 100644
index 0000000..f2e8a8d
--- /dev/null
+++ b/none/tests/x86-linux/bug345887.c
@@ -0,0 +1,33 @@
+/* This test used to cause an assertion in the address space manager */
+
+__attribute__((noinline))
+static void inner(void)
+{
+   /* Set other registers to apriori known values. */
+   __asm__ __volatile__(
+      "movl $0x101, %%eax\n"
+      "movl $0x102, %%ebx\n"
+      "movl $0x103, %%ecx\n"
+      "movl $0x104, %%edx\n"
+      "movl $0x105, %%esi\n"
+      "movl $0x106, %%edi\n"
+      // not %ebp as mdb is then not able to reconstruct stack trace
+      "movl $0x108, %%esp\n"
+      "movl $0x1234, (%%eax)\n"  // should cause SEGV here
+      "ud2"                      // should never get here
+      : // no output registers
+      : // no input registers
+      : "memory", "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "%esp");
+}
+
+__attribute__((noinline))
+static void outer(void)
+{
+   inner();
+}
+
+int main(int argc, const char *argv[])
+{
+   outer();
+   return 0;
+}
diff --git a/none/tests/x86-linux/bug345887.stderr.exp b/none/tests/x86-linux/bug345887.stderr.exp
new file mode 100644
index 0000000..129193c
--- /dev/null
+++ b/none/tests/x86-linux/bug345887.stderr.exp
@@ -0,0 +1,9 @@
+
+Process terminating with default action of signal 11 (SIGSEGV)
+ Access not within mapped region at address 0x........
+   at 0x........: inner (bug345887.c:7)
+ If you believe this happened as a result of a stack
+ overflow in your program's main thread (unlikely but
+ possible), you can try to increase the size of the
+ main thread stack using the --main-stacksize= flag.
+ The main thread stack size used in this run was ....
diff --git a/none/tests/x86-linux/bug345887.vgtest b/none/tests/x86-linux/bug345887.vgtest
new file mode 100644
index 0000000..c014423
--- /dev/null
+++ b/none/tests/x86-linux/bug345887.vgtest
@@ -0,0 +1,4 @@
+prog: bug345887
+vgopts: -q
+stderr_filter: filter_minimal
+cleanup: rm -f vgcore.*
diff --git a/none/tests/x86-linux/filter_minimal b/none/tests/x86-linux/filter_minimal
new file mode 100755
index 0000000..e69398c
--- /dev/null
+++ b/none/tests/x86-linux/filter_minimal
@@ -0,0 +1,20 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+# Remove ==pid== and **pid** strings 
+perl -p -e 's/(==|\*\*)[0-9]{1,7}\1 //' |
+
+perl -p -e 's/0x[0-9A-Fa-f]+/0x......../g' |
+
+# Older bash versions print abnormal termination messages on the stderr
+# of the bash process. Newer bash versions redirect such messages properly.
+# Suppress any redirected abnormal termination messages. You can find the
+# complete list of messages in the bash source file siglist.c.
+perl -n -e 'print if !/^(Segmentation fault|Alarm clock|Aborted|Bus error)( \(core dumped\))?$/' |
+
+# Remove the size in "The main thread stack size..." message.
+sed "s/The main thread stack size used in this run was [0-9]*/The main thread stack size used in this run was .../"
+
+# NOTE: it is essential for the bug345887 testcase that the stderr
+#       filtering does *not* remove lines beginning with --