Blame string/tst-xbzero-opt.c

Packit Service 82fcde
/* Test that explicit_bzero block clears are not optimized out.
Packit Service 82fcde
   Copyright (C) 2016-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
/* This test is conceptually based on a test designed by Matthew
Packit Service 82fcde
   Dempsky for the OpenBSD regression suite:
Packit Service 82fcde
   <openbsd>/src/regress/lib/libc/explicit_bzero/explicit_bzero.c.
Packit Service 82fcde
   The basic idea is, we have a function that contains a
Packit Service 82fcde
   block-clearing operation (not necessarily explicit_bzero), after
Packit Service 82fcde
   which the block is dead, in the compiler-jargon sense.  Execute
Packit Service 82fcde
   that function while running on a user-allocated alternative
Packit Service 82fcde
   stack. Then we have another pointer to the memory region affected
Packit Service 82fcde
   by the block clear -- namely, the original allocation for the
Packit Service 82fcde
   alternative stack -- and can find out whether it actually happened.
Packit Service 82fcde
Packit Service 82fcde
   The OpenBSD test uses sigaltstack and SIGUSR1 to get onto an
Packit Service 82fcde
   alternative stack.  This causes a number of awkward problems; some
Packit Service 82fcde
   operating systems (e.g. Solaris and OSX) wipe the signal stack upon
Packit Service 82fcde
   returning to the normal stack, there's no way to be sure that other
Packit Service 82fcde
   processes running on the same system will not interfere, and the
Packit Service 82fcde
   signal stack is very small so it's not safe to call printf there.
Packit Service 82fcde
   This implementation instead uses the <ucontext.h> coroutine
Packit Service 82fcde
   interface.  The coroutine stack is still too small to safely use
Packit Service 82fcde
   printf, but we know the OS won't erase it, so we can do all the
Packit Service 82fcde
   checks and printing from the normal stack.  */
Packit Service 82fcde
Packit Service 82fcde
#define _GNU_SOURCE 1
Packit Service 82fcde
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <signal.h>
Packit Service 82fcde
#include <stdio.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <ucontext.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
Packit Service 82fcde
/* A byte pattern that is unlikely to occur by chance: the first 16
Packit Service 82fcde
   prime numbers (OEIS A000040).  */
Packit Service 82fcde
static const unsigned char test_pattern[16] =
Packit Service 82fcde
{
Packit Service 82fcde
  2, 3, 5, 7,  11, 13, 17, 19,  23, 29, 31, 37,  41, 43, 47, 53
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
/* Immediately after each subtest returns, we call swapcontext to get
Packit Service 82fcde
   back onto the main stack.  That call might itself overwrite the
Packit Service 82fcde
   test pattern, so we fill a modest-sized buffer with copies of it
Packit Service 82fcde
   and check whether any of them survived.  */
Packit Service 82fcde
Packit Service 82fcde
#define PATTERN_SIZE (sizeof test_pattern)
Packit Service 82fcde
#define PATTERN_REPS 32
Packit Service 82fcde
#define TEST_BUFFER_SIZE (PATTERN_SIZE * PATTERN_REPS)
Packit Service 82fcde
Packit Service 82fcde
/* There are three subtests, two of which are sanity checks.
Packit Service 82fcde
   Each test follows this sequence:
Packit Service 82fcde
Packit Service 82fcde
     main                      coroutine
Packit Service 82fcde
     ----                      --------
Packit Service 82fcde
     advance cur_subtest
Packit Service 82fcde
     swap
Packit Service 82fcde
                               call setup function
Packit Service 82fcde
                                 prepare test buffer
Packit Service 82fcde
                                 swap
Packit Service 82fcde
     verify that buffer
Packit Service 82fcde
     was filled in
Packit Service 82fcde
     swap
Packit Service 82fcde
                                 possibly clear buffer
Packit Service 82fcde
                                 return
Packit Service 82fcde
                               swap
Packit Service 82fcde
     check buffer again,
Packit Service 82fcde
     according to test
Packit Service 82fcde
     expectation
Packit Service 82fcde
Packit Service 82fcde
   In the "no_clear" case, we don't do anything to the test buffer
Packit Service 82fcde
   between preparing it and letting it go out of scope, and we expect
Packit Service 82fcde
   to find it.  This confirms that the test buffer does get filled in
Packit Service 82fcde
   and we can find it from the stack buffer.  In the "ordinary_clear"
Packit Service 82fcde
   case, we clear it using memset.  Depending on the target, the
Packit Service 82fcde
   compiler may not be able to apply dead store elimination to the
Packit Service 82fcde
   memset call, so the test does not fail if the memset is not
Packit Service 82fcde
   eliminated.  Finally, the "explicit_clear" case uses explicit_bzero
Packit Service 82fcde
   and expects _not_ to find the test buffer, which is the real
Packit Service 82fcde
   test.  */
Packit Service 82fcde
Packit Service 82fcde
static ucontext_t uc_main, uc_co;
Packit Service 82fcde
Packit Service 82fcde
static __attribute__ ((noinline, noclone)) int
Packit Service 82fcde
use_test_buffer (unsigned char *buf)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned int sum = 0;
Packit Service 82fcde
Packit Service 82fcde
  for (unsigned int i = 0; i < PATTERN_REPS; i++)
Packit Service 82fcde
    sum += buf[i * PATTERN_SIZE];
Packit Service 82fcde
Packit Service 82fcde
  return (sum == 2 * PATTERN_REPS) ? 0 : 1;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Always check the test buffer immediately after filling it; this
Packit Service 82fcde
   makes externally visible side effects depend on the buffer existing
Packit Service 82fcde
   and having been filled in.  */
Packit Service 82fcde
#if defined __CET__ && !__glibc_has_attribute (__indirect_return__)
Packit Service 82fcde
/* Note: swapcontext returns via indirect branch when SHSTK is enabled.
Packit Service 82fcde
   Without indirect_return attribute, swapcontext is marked with
Packit Service 82fcde
   returns_twice attribute, which prevents always_inline to work.  */
Packit Service 82fcde
# define ALWAYS_INLINE
Packit Service 82fcde
#else
Packit Service 82fcde
# define ALWAYS_INLINE	__attribute__ ((always_inline))
Packit Service 82fcde
#endif
Packit Service 82fcde
static inline ALWAYS_INLINE void
Packit Service 82fcde
prepare_test_buffer (unsigned char *buf)
Packit Service 82fcde
{
Packit Service 82fcde
  for (unsigned int i = 0; i < PATTERN_REPS; i++)
Packit Service 82fcde
    memcpy (buf + i*PATTERN_SIZE, test_pattern, PATTERN_SIZE);
Packit Service 82fcde
Packit Service 82fcde
  if (swapcontext (&uc_co, &uc_main))
Packit Service 82fcde
    abort ();
Packit Service 82fcde
Packit Service 82fcde
  /* Force the compiler to really copy the pattern to buf.  */
Packit Service 82fcde
  if (use_test_buffer (buf))
Packit Service 82fcde
    abort ();
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
setup_no_clear (void)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned char buf[TEST_BUFFER_SIZE];
Packit Service 82fcde
  prepare_test_buffer (buf);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
setup_ordinary_clear (void)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned char buf[TEST_BUFFER_SIZE];
Packit Service 82fcde
  prepare_test_buffer (buf);
Packit Service 82fcde
  memset (buf, 0, TEST_BUFFER_SIZE);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
setup_explicit_clear (void)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned char buf[TEST_BUFFER_SIZE];
Packit Service 82fcde
  prepare_test_buffer (buf);
Packit Service 82fcde
  explicit_bzero (buf, TEST_BUFFER_SIZE);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
enum test_expectation
Packit Service 82fcde
  {
Packit Service 82fcde
    EXPECT_NONE, EXPECT_SOME, EXPECT_ALL, NO_EXPECTATIONS
Packit Service 82fcde
  };
Packit Service 82fcde
struct subtest
Packit Service 82fcde
{
Packit Service 82fcde
  void (*setup_subtest) (void);
Packit Service 82fcde
  const char *label;
Packit Service 82fcde
  enum test_expectation expected;
Packit Service 82fcde
};
Packit Service 82fcde
static const struct subtest *cur_subtest;
Packit Service 82fcde
Packit Service 82fcde
static const struct subtest subtests[] =
Packit Service 82fcde
{
Packit Service 82fcde
  { setup_no_clear,       "no clear",       EXPECT_SOME },
Packit Service 82fcde
  /* The memset may happen or not, depending on compiler
Packit Service 82fcde
     optimizations.  */
Packit Service 82fcde
  { setup_ordinary_clear, "ordinary clear", NO_EXPECTATIONS },
Packit Service 82fcde
  { setup_explicit_clear, "explicit clear", EXPECT_NONE },
Packit Service 82fcde
  { 0,                    0,                -1          }
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
test_coroutine (void)
Packit Service 82fcde
{
Packit Service 82fcde
  while (cur_subtest->setup_subtest)
Packit Service 82fcde
    {
Packit Service 82fcde
      cur_subtest->setup_subtest ();
Packit Service 82fcde
      if (swapcontext (&uc_co, &uc_main))
Packit Service 82fcde
	abort ();
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* All the code above this point runs on the coroutine stack.
Packit Service 82fcde
   All the code below this point runs on the main stack.  */
Packit Service 82fcde
Packit Service 82fcde
static int test_status;
Packit Service 82fcde
static unsigned char *co_stack_buffer;
Packit Service 82fcde
static size_t co_stack_size;
Packit Service 82fcde
Packit Service 82fcde
static unsigned int
Packit Service 82fcde
count_test_patterns (unsigned char *buf, size_t bufsiz)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned char *first = memmem (buf, bufsiz, test_pattern, PATTERN_SIZE);
Packit Service 82fcde
  if (!first)
Packit Service 82fcde
    return 0;
Packit Service 82fcde
  unsigned int cnt = 0;
Packit Service 82fcde
  for (unsigned int i = 0; i < PATTERN_REPS; i++)
Packit Service 82fcde
    {
Packit Service 82fcde
      unsigned char *p = first + i*PATTERN_SIZE;
Packit Service 82fcde
      if (p + PATTERN_SIZE - buf > bufsiz)
Packit Service 82fcde
	break;
Packit Service 82fcde
      if (memcmp (p, test_pattern, PATTERN_SIZE) == 0)
Packit Service 82fcde
	cnt++;
Packit Service 82fcde
    }
Packit Service 82fcde
  return cnt;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
check_test_buffer (enum test_expectation expected,
Packit Service 82fcde
		   const char *label, const char *stage)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned int cnt = count_test_patterns (co_stack_buffer, co_stack_size);
Packit Service 82fcde
  switch (expected)
Packit Service 82fcde
    {
Packit Service 82fcde
    case EXPECT_NONE:
Packit Service 82fcde
      if (cnt == 0)
Packit Service 82fcde
	printf ("PASS: %s/%s: expected 0 got %d\n", label, stage, cnt);
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  printf ("FAIL: %s/%s: expected 0 got %d\n", label, stage, cnt);
Packit Service 82fcde
	  test_status = 1;
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
    case EXPECT_SOME:
Packit Service 82fcde
      if (cnt > 0)
Packit Service 82fcde
	printf ("PASS: %s/%s: expected some got %d\n", label, stage, cnt);
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  printf ("FAIL: %s/%s: expected some got 0\n", label, stage);
Packit Service 82fcde
	  test_status = 1;
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
     case EXPECT_ALL:
Packit Service 82fcde
      if (cnt == PATTERN_REPS)
Packit Service 82fcde
	printf ("PASS: %s/%s: expected %d got %d\n", label, stage,
Packit Service 82fcde
		PATTERN_REPS, cnt);
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  printf ("FAIL: %s/%s: expected %d got %d\n", label, stage,
Packit Service 82fcde
		  PATTERN_REPS, cnt);
Packit Service 82fcde
	  test_status = 1;
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
    case NO_EXPECTATIONS:
Packit Service 82fcde
      printf ("INFO: %s/%s: found %d patterns%s\n", label, stage, cnt,
Packit Service 82fcde
	      cnt == 0 ? " (memset not eliminated)" : "");
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
    default:
Packit Service 82fcde
      printf ("ERROR: %s/%s: invalid value for 'expected' = %d\n",
Packit Service 82fcde
	      label, stage, (int)expected);
Packit Service 82fcde
      test_status = 1;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
test_loop (void)
Packit Service 82fcde
{
Packit Service 82fcde
  cur_subtest = subtests;
Packit Service 82fcde
  while (cur_subtest->setup_subtest)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (swapcontext (&uc_main, &uc_co))
Packit Service 82fcde
	abort ();
Packit Service 82fcde
      check_test_buffer (EXPECT_ALL, cur_subtest->label, "prepare");
Packit Service 82fcde
      if (swapcontext (&uc_main, &uc_co))
Packit Service 82fcde
	abort ();
Packit Service 82fcde
      check_test_buffer (cur_subtest->expected, cur_subtest->label, "test");
Packit Service 82fcde
      cur_subtest++;
Packit Service 82fcde
    }
Packit Service 82fcde
  /* Terminate the coroutine.  */
Packit Service 82fcde
  if (swapcontext (&uc_main, &uc_co))
Packit Service 82fcde
    abort ();
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
do_test (void)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t page_alignment = sysconf (_SC_PAGESIZE);
Packit Service 82fcde
  if (page_alignment < sizeof (void *))
Packit Service 82fcde
    page_alignment = sizeof (void *);
Packit Service 82fcde
Packit Service 82fcde
  co_stack_size = SIGSTKSZ + TEST_BUFFER_SIZE;
Packit Service 82fcde
  if (co_stack_size < page_alignment * 4)
Packit Service 82fcde
    co_stack_size = page_alignment * 4;
Packit Service 82fcde
Packit Service 82fcde
  void *p;
Packit Service 82fcde
  int err = posix_memalign (&p, page_alignment, co_stack_size);
Packit Service 82fcde
  if (err || !p)
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("ERROR: allocating alt stack: %s\n", strerror (err));
Packit Service 82fcde
      return 2;
Packit Service 82fcde
    }
Packit Service 82fcde
  co_stack_buffer = p;
Packit Service 82fcde
Packit Service 82fcde
  if (getcontext (&uc_co))
Packit Service 82fcde
    {
Packit Service 82fcde
      printf ("ERROR: allocating coroutine context: %s\n", strerror (err));
Packit Service 82fcde
      return 2;
Packit Service 82fcde
    }
Packit Service 82fcde
  uc_co.uc_stack.ss_sp   = co_stack_buffer;
Packit Service 82fcde
  uc_co.uc_stack.ss_size = co_stack_size;
Packit Service 82fcde
  uc_co.uc_link          = &uc_main;
Packit Service 82fcde
  makecontext (&uc_co, test_coroutine, 0);
Packit Service 82fcde
Packit Service 82fcde
  test_loop ();
Packit Service 82fcde
  return test_status;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#include <support/test-driver.c>