mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-11-03 20:53:13 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			162 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Make sure that the stackaddr returned by pthread_getattr_np is
 | 
						|
   reachable.
 | 
						|
 | 
						|
   Copyright (C) 2012-2014 Free Software Foundation, Inc.
 | 
						|
   This file is part of the GNU C Library.
 | 
						|
 | 
						|
   The GNU C Library is free software; you can redistribute it and/or
 | 
						|
   modify it under the terms of the GNU Lesser General Public
 | 
						|
   License as published by the Free Software Foundation; either
 | 
						|
   version 2.1 of the License, or (at your option) any later version.
 | 
						|
 | 
						|
   The GNU C Library is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
   Lesser General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU Lesser General Public
 | 
						|
   License along with the GNU C Library; if not, see
 | 
						|
   <http://www.gnu.org/licenses/>.  */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sys/resource.h>
 | 
						|
#include <sys/param.h>
 | 
						|
#include <pthread.h>
 | 
						|
#include <alloca.h>
 | 
						|
#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)
 | 
						|
{
 | 
						|
  volatile char *mem = (char *) &mem;
 | 
						|
  /* FIXME:  mem >= target for _STACK_GROWSUP.  */
 | 
						|
  mem = alloca ((size_t) (mem - target));
 | 
						|
 | 
						|
  *mem = 42;
 | 
						|
  return mem;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_self_pthread_attr (const char *id, void **stackaddr, size_t *stacksize)
 | 
						|
{
 | 
						|
  pthread_attr_t attr;
 | 
						|
  int ret;
 | 
						|
  pthread_t me = pthread_self ();
 | 
						|
 | 
						|
  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)) < 0)
 | 
						|
    {
 | 
						|
      printf ("%s: pthread_attr_getstack returned error: %s\n", id,
 | 
						|
	      strerror (ret));
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Verify that the stack size returned by pthread_getattr_np is usable when
 | 
						|
   the returned value is subject to rlimit.  */
 | 
						|
static int
 | 
						|
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");
 | 
						|
 | 
						|
  ret = getrlimit (RLIMIT_STACK, &stack_limit);
 | 
						|
  if (ret)
 | 
						|
    {
 | 
						|
      perror ("getrlimit failed");
 | 
						|
      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
 | 
						|
     (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;
 | 
						|
    }
 | 
						|
 | 
						|
  if (get_self_pthread_attr ("check_stack_top2", &stackaddr, &stacksize))
 | 
						|
    return 1;
 | 
						|
 | 
						|
  printf ("Adjusted rlimit: stacksize=%zu, stackaddr=%p\n", stacksize,
 | 
						|
          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");
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* TODO: Similar check for thread stacks once the thread stack sizes are
 | 
						|
   fixed.  */
 | 
						|
static int
 | 
						|
do_test (void)
 | 
						|
{
 | 
						|
  pagesize = sysconf (_SC_PAGESIZE);
 | 
						|
  return check_stack_top ();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#define TEST_FUNCTION do_test ()
 | 
						|
#include "../test-skeleton.c"
 |