Handle the PROT_GROWSDOWN and PROT_GROWSUP mprotect flags properly
which stops glibc falling over when a program requires an executable
stack and glibc has been built to assume that PROT_GROWSDOWN will work.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5041 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_syswrap/syswrap-generic.c b/coregrind/m_syswrap/syswrap-generic.c
index 4c2bd7b..0b5e10b 100644
--- a/coregrind/m_syswrap/syswrap-generic.c
+++ b/coregrind/m_syswrap/syswrap-generic.c
@@ -4552,8 +4552,43 @@
    PRE_REG_READ3(long, "mprotect",
                  unsigned long, addr, vki_size_t, len, unsigned long, prot);
 
-   if (!ML_(valid_client_addr)(ARG1, ARG2, tid, "mprotect"))
+   if (!ML_(valid_client_addr)(ARG1, ARG2, tid, "mprotect")) {
       SET_STATUS_Failure( VKI_ENOMEM );
+   } else if (ARG3 & (VKI_PROT_GROWSDOWN|VKI_PROT_GROWSUP)) {
+      UInt grows = ARG3 & (VKI_PROT_GROWSDOWN|VKI_PROT_GROWSUP);
+      NSegment *aseg = VG_(am_find_nsegment)(ARG1);
+      NSegment *rseg;
+
+      vg_assert(aseg);
+
+      if (grows == VKI_PROT_GROWSDOWN) {
+         rseg = VG_(am_next_nsegment)( aseg, False/*backwards*/ );
+         if (rseg &&
+             rseg->kind == SkResvn &&
+             rseg->smode == SmUpper &&
+             rseg->end+1 == aseg->start) {
+            Addr end = ARG1 + ARG2;
+            ARG1 = aseg->start;
+            ARG2 = end - aseg->start;
+            ARG3 &= ~VKI_PROT_GROWSDOWN;
+         } else {
+            SET_STATUS_Failure( VKI_EINVAL );
+         }
+      } else if (grows == VKI_PROT_GROWSUP) {
+         rseg = VG_(am_next_nsegment)( aseg, True/*forwards*/ );
+         if (rseg &&
+             rseg->kind == SkResvn &&
+             rseg->smode == SmLower &&
+             aseg->end+1 == rseg->start) {
+            ARG2 = aseg->end - ARG1 + 1;
+            ARG3 &= ~VKI_PROT_GROWSUP;
+         } else {
+            SET_STATUS_Failure( VKI_EINVAL );
+         }
+      } else {
+         SET_STATUS_Failure( VKI_EINVAL );
+      }
+   }
 }
 
 POST(sys_mprotect)
diff --git a/include/vki-amd64-linux.h b/include/vki-amd64-linux.h
index e4277ad..79f96a4 100644
--- a/include/vki-amd64-linux.h
+++ b/include/vki-amd64-linux.h
@@ -216,6 +216,8 @@
 #define VKI_PROT_WRITE	0x2		/* page can be written */
 #define VKI_PROT_EXEC	0x4		/* page can be executed */
 #define VKI_PROT_NONE	0x0		/* page can not be accessed */
+#define VKI_PROT_GROWSDOWN	0x01000000	/* mprotect flag: extend change to start of growsdown vma */
+#define VKI_PROT_GROWSUP	0x02000000	/* mprotect flag: extend change to end of growsup vma */
 
 #define VKI_MAP_SHARED	0x01		/* Share changes */
 #define VKI_MAP_PRIVATE	0x02		/* Changes are private */
diff --git a/include/vki-ppc32-linux.h b/include/vki-ppc32-linux.h
index 7f8b752..d5ade89 100644
--- a/include/vki-ppc32-linux.h
+++ b/include/vki-ppc32-linux.h
@@ -237,6 +237,8 @@
 #define VKI_PROT_READ		0x1      /* page can be read */
 #define VKI_PROT_WRITE		0x2      /* page can be written */
 #define VKI_PROT_EXEC		0x4      /* page can be executed */
+#define VKI_PROT_GROWSDOWN	0x01000000	/* mprotect flag: extend change to start of growsdown vma */
+#define VKI_PROT_GROWSUP	0x02000000	/* mprotect flag: extend change to end of growsup vma */
 
 #define VKI_MAP_SHARED		0x01     /* Share changes */
 #define VKI_MAP_PRIVATE		0x02     /* Changes are private */
diff --git a/include/vki-x86-linux.h b/include/vki-x86-linux.h
index ae73305..5d8d329 100644
--- a/include/vki-x86-linux.h
+++ b/include/vki-x86-linux.h
@@ -246,6 +246,8 @@
 #define VKI_PROT_READ	0x1		/* page can be read */
 #define VKI_PROT_WRITE	0x2		/* page can be written */
 #define VKI_PROT_EXEC	0x4		/* page can be executed */
+#define VKI_PROT_GROWSDOWN	0x01000000	/* mprotect flag: extend change to start of growsdown vma */
+#define VKI_PROT_GROWSUP	0x02000000	/* mprotect flag: extend change to end of growsup vma */
 
 #define VKI_MAP_SHARED	0x01		/* Share changes */
 #define VKI_MAP_PRIVATE	0x02		/* Changes are private */