Blame nptl/tst-pthread-getattr.c

Packit 6c4009
/* Make sure that the stackaddr returned by pthread_getattr_np is
Packit 6c4009
   reachable.
Packit 6c4009
Packit 6c4009
   Copyright (C) 2012-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <sys/resource.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <alloca.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
Packit 6c4009
/* There is an obscure bug in the kernel due to which RLIMIT_STACK is sometimes
Packit 6c4009
   returned as unlimited when it is not, which may cause this test to fail.
Packit 6c4009
   There is also the other case where RLIMIT_STACK is intentionally set as
Packit 6c4009
   unlimited or very high, which may result in a vma that is too large and again
Packit 6c4009
   results in a test case failure.  To avoid these problems, we cap the stack
Packit 6c4009
   size to one less than 8M.  See the following mailing list threads for more
Packit 6c4009
   information about this problem:
Packit 6c4009
   <https://sourceware.org/ml/libc-alpha/2012-06/msg00599.html>
Packit 6c4009
   <https://sourceware.org/ml/libc-alpha/2012-06/msg00713.html>.  */
Packit 6c4009
#define MAX_STACK_SIZE (8192 * 1024 - 1)
Packit 6c4009
Packit 6c4009
static size_t pagesize;
Packit 6c4009
Packit 6c4009
/* Check if the page in which TARGET lies is accessible.  This will segfault
Packit 6c4009
   if it fails.  */
Packit 6c4009
static volatile char *
Packit 6c4009
allocate_and_test (char *target)
Packit 6c4009
{
Packit 6c4009
  volatile char *mem = (char *) &mem;
Packit 6c4009
  /* FIXME:  mem >= target for _STACK_GROWSUP.  */
Packit 6c4009
  mem = alloca ((size_t) (mem - target));
Packit 6c4009
Packit 6c4009
  *mem = 42;
Packit 6c4009
  return mem;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
get_self_pthread_attr (const char *id, void **stackaddr, size_t *stacksize)
Packit 6c4009
{
Packit 6c4009
  pthread_attr_t attr;
Packit 6c4009
  int ret;
Packit 6c4009
  pthread_t me = pthread_self ();
Packit 6c4009
Packit 6c4009
  if ((ret = pthread_getattr_np (me, &attr)) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("%s: pthread_getattr_np failed: %s\n", id, strerror (ret));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if ((ret = pthread_attr_getstack (&attr, stackaddr, stacksize)) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("%s: pthread_attr_getstack returned error: %s\n", id,
Packit 6c4009
	      strerror (ret));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Verify that the stack size returned by pthread_getattr_np is usable when
Packit 6c4009
   the returned value is subject to rlimit.  */
Packit 6c4009
static int
Packit 6c4009
check_stack_top (void)
Packit 6c4009
{
Packit 6c4009
  struct rlimit stack_limit;
Packit 6c4009
  void *stackaddr;
Packit 6c4009
  volatile void *mem;
Packit 6c4009
  size_t stacksize = 0;
Packit 6c4009
  int ret;
Packit 6c4009
  uintptr_t pagemask = ~(pagesize - 1);
Packit 6c4009
Packit 6c4009
  puts ("Verifying that stack top is accessible");
Packit 6c4009
Packit 6c4009
  ret = getrlimit (RLIMIT_STACK, &stack_limit);
Packit 6c4009
  if (ret)
Packit 6c4009
    {
Packit 6c4009
      perror ("getrlimit failed");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  printf ("current rlimit_stack is %zu\n", (size_t) stack_limit.rlim_cur);
Packit 6c4009
Packit 6c4009
  if (get_self_pthread_attr ("check_stack_top", &stackaddr, &stacksize))
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  /* Reduce the rlimit to a page less that what is currently being returned
Packit 6c4009
     (subject to a maximum of MAX_STACK_SIZE) so that we ensure that
Packit 6c4009
     pthread_getattr_np uses rlimit.  The figure is intentionally unaligned so
Packit 6c4009
     to verify that pthread_getattr_np returns an aligned stacksize that
Packit 6c4009
     correctly fits into the rlimit.  We don't bother about the case where the
Packit 6c4009
     stack is limited by the vma below it and not by the rlimit because the
Packit 6c4009
     stacksize returned in that case is computed from the end of that vma and is
Packit 6c4009
     hence safe.  */
Packit 6c4009
  stack_limit.rlim_cur = MIN (stacksize - pagesize + 1, MAX_STACK_SIZE);
Packit 6c4009
  printf ("Adjusting RLIMIT_STACK to %zu\n", (size_t) stack_limit.rlim_cur);
Packit 6c4009
  if ((ret = setrlimit (RLIMIT_STACK, &stack_limit)) < 0)
Packit 6c4009
    {
Packit 6c4009
      perror ("setrlimit failed");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (get_self_pthread_attr ("check_stack_top2", &stackaddr, &stacksize))
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  printf ("Adjusted rlimit: stacksize=%zu, stackaddr=%p\n", stacksize,
Packit 6c4009
          stackaddr);
Packit 6c4009
Packit 6c4009
  /* A lot of targets tend to write stuff on top of the user stack during
Packit 6c4009
     context switches, so we cannot possibly safely go up to the very top of
Packit 6c4009
     stack and test access there.  It is however sufficient to simply check if
Packit 6c4009
     the top page is accessible, so we target our access halfway up the top
Packit 6c4009
     page.  Thanks Chris Metcalf for this idea.  */
Packit 6c4009
  mem = allocate_and_test (stackaddr + pagesize / 2);
Packit 6c4009
Packit 6c4009
  /* Before we celebrate, make sure we actually did test the same page.  */
Packit 6c4009
  if (((uintptr_t) stackaddr & pagemask) != ((uintptr_t) mem & pagemask))
Packit 6c4009
    {
Packit 6c4009
      printf ("We successfully wrote into the wrong page.\n"
Packit 6c4009
	      "Expected %#" PRIxPTR ", but got %#" PRIxPTR "\n",
Packit 6c4009
	      (uintptr_t) stackaddr & pagemask, (uintptr_t) mem & pagemask);
Packit 6c4009
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  puts ("Stack top tests done");
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* TODO: Similar check for thread stacks once the thread stack sizes are
Packit 6c4009
   fixed.  */
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  pagesize = sysconf (_SC_PAGESIZE);
Packit 6c4009
  return check_stack_top ();
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#define TEST_FUNCTION do_test ()
Packit 6c4009
#include "../test-skeleton.c"