aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nptl/ChangeLog11
-rw-r--r--nptl/tst-pthread-getattr.c85
2 files changed, 74 insertions, 22 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 2f38bcc..6969fd2 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,14 @@
+2012-07-28 Siddhesh Poyarekar <siddhesh@redhat.com>
+
+ * tst-pthread-getattr.c (MAX_STACK_SIZE): New macro.
+ (pagesize): New static variable.
+ (allocate_and_test): Return MEM. Rename parameter to TARGET.
+ (check_stack_top): New local variables MEM and PAGEMASK. Cap
+ stack size to MAX_STACK_SIZE. Call allocate_and_test for
+ halfway up the stack top page. Verify that the top page was
+ written into.
+ (do_test): Get pagesize using sysconf.
+
2012-07-25 Andreas Schwab <schwab@linux-m68k.org>
* sysdeps/unix/sysv/linux/i386/pt-vfork.S: Remove pseudo_end
diff --git a/nptl/tst-pthread-getattr.c b/nptl/tst-pthread-getattr.c
index 6f2cfc6..5a619fa 100644
--- a/nptl/tst-pthread-getattr.c
+++ b/nptl/tst-pthread-getattr.c
@@ -21,18 +21,37 @@
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
+#include <sys/param.h>
#include <pthread.h>
#include <alloca.h>
-
-/* Move the stack pointer so that stackaddr is accessible and then check if it
- really is accessible. This will segfault if it fails. */
-static void
-allocate_and_test (void *stackaddr)
+#include <assert.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+/* There is an obscure bug in the kernel due to which RLIMIT_STACK is sometimes
+ returned as unlimited when it is not, which may cause this test to fail.
+ There is also the other case where RLIMIT_STACK is intentionally set as
+ unlimited or very high, which may result in a vma that is too large and again
+ results in a test case failure. To avoid these problems, we cap the stack
+ size to one less than 8M. See the following mailing list threads for more
+ information about this problem:
+ <http://sourceware.org/ml/libc-alpha/2012-06/msg00599.html>
+ <http://sourceware.org/ml/libc-alpha/2012-06/msg00713.html>. */
+#define MAX_STACK_SIZE (8192 * 1024 - 1)
+
+static size_t pagesize;
+
+/* Check if the page in which TARGET lies is accessible. This will segfault
+ if it fails. */
+static volatile char *
+allocate_and_test (char *target)
{
- void *mem = &mem;
- /* FIXME: The difference will be negative for _STACK_GROWSUP. */
- mem = alloca ((size_t) (mem - stackaddr));
- *(int *)(mem) = 0;
+ volatile char *mem = (char *) &mem;
+ /* FIXME: mem >= target for _STACK_GROWSUP. */
+ mem = alloca ((size_t) (mem - target));
+
+ *mem = 42;
+ return mem;
}
static int
@@ -42,13 +61,13 @@ get_self_pthread_attr (const char *id, void **stackaddr, size_t *stacksize)
int ret;
pthread_t me = pthread_self ();
- if ((ret = pthread_getattr_np (me, &attr)))
+ if ((ret = pthread_getattr_np (me, &attr)) < 0)
{
printf ("%s: pthread_getattr_np failed: %s\n", id, strerror (ret));
return 1;
}
- if ((ret = pthread_attr_getstack (&attr, stackaddr, stacksize)))
+ if ((ret = pthread_attr_getstack (&attr, stackaddr, stacksize)) < 0)
{
printf ("%s: pthread_attr_getstack returned error: %s\n", id,
strerror (ret));
@@ -65,8 +84,10 @@ check_stack_top (void)
{
struct rlimit stack_limit;
void *stackaddr;
+ volatile void *mem;
size_t stacksize = 0;
int ret;
+ uintptr_t pagemask = ~(pagesize - 1);
puts ("Verifying that stack top is accessible");
@@ -77,19 +98,22 @@ check_stack_top (void)
return 1;
}
+ printf ("current rlimit_stack is %zu\n", (size_t) stack_limit.rlim_cur);
+
if (get_self_pthread_attr ("check_stack_top", &stackaddr, &stacksize))
return 1;
- /* Reduce the rlimit to a page less that what is currently being returned so
- that we ensure that pthread_getattr_np uses rlimit. The figure is
- intentionally unaligned so to verify that pthread_getattr_np returns an
- aligned stacksize that correctly fits into the rlimit. We don't bother
- about the case where the stack is limited by the vma below it and not by
- the rlimit because the stacksize returned in that case is computed from
- the end of that vma and is hence safe. */
- stack_limit.rlim_cur = stacksize - 4095;
- printf ("Adjusting RLIMIT_STACK to %zu\n", stack_limit.rlim_cur);
- if ((ret = setrlimit (RLIMIT_STACK, &stack_limit)))
+ /* Reduce the rlimit to a page less that what is currently being returned
+ (subject to a maximum of MAX_STACK_SIZE) so that we ensure that
+ pthread_getattr_np uses rlimit. The figure is intentionally unaligned so
+ to verify that pthread_getattr_np returns an aligned stacksize that
+ correctly fits into the rlimit. We don't bother about the case where the
+ stack is limited by the vma below it and not by the rlimit because the
+ stacksize returned in that case is computed from the end of that vma and is
+ hence safe. */
+ stack_limit.rlim_cur = MIN (stacksize - pagesize + 1, MAX_STACK_SIZE);
+ printf ("Adjusting RLIMIT_STACK to %zu\n", (size_t) stack_limit.rlim_cur);
+ if ((ret = setrlimit (RLIMIT_STACK, &stack_limit)) < 0)
{
perror ("setrlimit failed");
return 1;
@@ -100,7 +124,23 @@ check_stack_top (void)
printf ("Adjusted rlimit: stacksize=%zu, stackaddr=%p\n", stacksize,
stackaddr);
- allocate_and_test (stackaddr);
+
+ /* A lot of targets tend to write stuff on top of the user stack during
+ context switches, so we cannot possibly safely go up to the very top of
+ stack and test access there. It is however sufficient to simply check if
+ the top page is accessible, so we target our access halfway up the top
+ page. Thanks Chris Metcalf for this idea. */
+ mem = allocate_and_test (stackaddr + pagesize / 2);
+
+ /* Before we celebrate, make sure we actually did test the same page. */
+ if (((uintptr_t) stackaddr & pagemask) != ((uintptr_t) mem & pagemask))
+ {
+ printf ("We successfully wrote into the wrong page.\n"
+ "Expected %#" PRIxPTR ", but got %#" PRIxPTR "\n",
+ (uintptr_t) stackaddr & pagemask, (uintptr_t) mem & pagemask);
+
+ return 1;
+ }
puts ("Stack top tests done");
@@ -112,6 +152,7 @@ check_stack_top (void)
static int
do_test (void)
{
+ pagesize = sysconf (_SC_PAGESIZE);
return check_stack_top ();
}