(root)/
libxcrypt-4.4.36/
test/
explicit-bzero.c
       1  /* Test that explicit_bzero block clears are not optimized out.
       2     Copyright (C) 2016-2020 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* This test is conceptually based on a test designed by Matthew
      20     Dempsky for the OpenBSD regression suite:
      21     <openbsd>/src/regress/lib/libc/explicit_bzero/explicit_bzero.c.
      22     The basic idea is, we have a function that contains a
      23     block-clearing operation (not necessarily explicit_bzero), after
      24     which the block is dead, in the compiler-jargon sense.  Execute
      25     that function while running on a user-allocated alternative
      26     stack. Then we have another pointer to the memory region affected
      27     by the block clear -- namely, the original allocation for the
      28     alternative stack -- and can find out whether it actually happened.
      29  
      30     The OpenBSD test uses sigaltstack and SIGUSR1 to get onto an
      31     alternative stack.  This causes a number of awkward problems; some
      32     operating systems (e.g. Solaris and OSX) wipe the signal stack upon
      33     returning to the normal stack, there's no way to be sure that other
      34     processes running on the same system will not interfere, and the
      35     signal stack is very small so it's not safe to call printf there.
      36     This implementation instead uses the <ucontext.h> coroutine
      37     interface.  The coroutine stack is still too small to safely use
      38     printf, but we know the OS won't erase it, so we can do all the
      39     checks and printing from the normal stack.  */
      40  
      41  #include "crypt-port.h"
      42  
      43  #ifndef USE_SWAPCONTEXT
      44  /* We can't do this test if we don't have the ucontext API.  */
      45  int main(void)
      46  {
      47    return 77;
      48  }
      49  #else
      50  
      51  #include <stdio.h>
      52  #include <stdlib.h>
      53  #include <ucontext.h>
      54  
      55  #ifdef HAVE_VALGRIND_VALGRIND_H
      56  # include <valgrind/valgrind.h>
      57  # include <valgrind/memcheck.h>
      58  #else
      59  # define VALGRIND_STACK_REGISTER(start, end) do {} while (0)
      60  # define VALGRIND_MAKE_MEM_DEFINED(addr, len) do {} while (0)
      61  #endif
      62  
      63  /* A byte pattern that is unlikely to occur by chance: the first 16
      64     prime numbers (OEIS A000040).  */
      65  static const unsigned char test_pattern[16] =
      66  {
      67    2, 3, 5, 7,  11, 13, 17, 19,  23, 29, 31, 37,  41, 43, 47, 53
      68  };
      69  
      70  /* Immediately after each subtest returns, we call swapcontext to get
      71     back onto the main stack.  That call might itself overwrite the
      72     test pattern, so we fill a modest-sized buffer with copies of it
      73     and check whether any of them survived.  */
      74  
      75  #define PATTERN_SIZE (sizeof test_pattern)
      76  #define PATTERN_REPS 32
      77  #define TEST_BUFFER_SIZE (PATTERN_SIZE * PATTERN_REPS)
      78  
      79  /* There are three subtests, two of which are sanity checks.
      80     Each test follows this sequence:
      81  
      82       main                      coroutine
      83       ----                      --------
      84       advance cur_subtest
      85       swap
      86                                 call setup function
      87                                   prepare test buffer
      88                                   swap
      89       verify that buffer
      90       was filled in
      91       swap
      92                                   possibly clear buffer
      93                                   return
      94                                 swap
      95       check buffer again,
      96       according to test
      97       expectation
      98  
      99     In the "no_clear" case, we don't do anything to the test buffer
     100     between preparing it and letting it go out of scope, and we expect
     101     to find it.  This confirms that the test buffer does get filled in
     102     and we can find it from the stack buffer.  In the "ordinary_clear"
     103     case, we clear it using memset.  Depending on the target, the
     104     compiler may not be able to apply dead store elimination to the
     105     memset call, so the test does not fail if the memset is not
     106     eliminated.  Finally, the "explicit_clear" case uses explicit_bzero
     107     and expects _not_ to find the test buffer, which is the real
     108     test.  */
     109  
     110  static ucontext_t uc_main, uc_co;
     111  
     112  static NO_INLINE int
     113  use_test_buffer (unsigned char *buf)
     114  {
     115    unsigned int sum = 0;
     116  
     117    for (unsigned int i = 0; i < PATTERN_REPS; i++)
     118      sum += buf[i * PATTERN_SIZE];
     119  
     120    return (sum == 2 * PATTERN_REPS) ? 0 : 1;
     121  }
     122  
     123  /* Always check the test buffer immediately after filling it; this
     124     makes externally visible side effects depend on the buffer existing
     125     and having been filled in.  */
     126  static inline void
     127  prepare_test_buffer (unsigned char *buf)
     128  {
     129    for (unsigned int i = 0; i < PATTERN_REPS; i++)
     130      memcpy (buf + i*PATTERN_SIZE, test_pattern, PATTERN_SIZE);
     131  
     132    if (swapcontext (&uc_co, &uc_main))
     133      abort ();
     134  
     135    /* Force the compiler to really copy the pattern to buf.  */
     136    if (use_test_buffer (buf))
     137      abort ();
     138  }
     139  
     140  static void
     141  setup_no_clear (void)
     142  {
     143    unsigned char buf[TEST_BUFFER_SIZE];
     144    prepare_test_buffer (buf);
     145  }
     146  
     147  static void
     148  setup_ordinary_clear (void)
     149  {
     150    unsigned char buf[TEST_BUFFER_SIZE];
     151    prepare_test_buffer (buf);
     152    memset (buf, 0, TEST_BUFFER_SIZE);
     153  }
     154  
     155  static void
     156  setup_explicit_clear (void)
     157  {
     158    unsigned char buf[TEST_BUFFER_SIZE];
     159    prepare_test_buffer (buf);
     160    explicit_bzero (buf, TEST_BUFFER_SIZE);
     161  }
     162  
     163  enum test_expectation
     164  {
     165    EXPECT_NONE = 1,
     166    EXPECT_SOME,
     167    EXPECT_ALL,
     168    NO_EXPECTATIONS,
     169    ARRAY_END = 0
     170  };
     171  struct subtest
     172  {
     173    void (*setup_subtest) (void);
     174    const char *label;
     175    enum test_expectation expected;
     176  };
     177  static const struct subtest *cur_subtest;
     178  
     179  static const struct subtest subtests[] =
     180  {
     181    { setup_no_clear,       "no clear",       EXPECT_SOME     },
     182    /* The memset may happen or not, depending on compiler
     183       optimizations.  */
     184    { setup_ordinary_clear, "ordinary clear", NO_EXPECTATIONS },
     185    { setup_explicit_clear, "explicit clear", EXPECT_NONE     },
     186    { 0,                    0,                ARRAY_END       }
     187  };
     188  
     189  static void
     190  test_coroutine (void)
     191  {
     192    while (cur_subtest->setup_subtest)
     193      {
     194        cur_subtest->setup_subtest ();
     195        if (swapcontext (&uc_co, &uc_main))
     196          abort ();
     197      }
     198  }
     199  
     200  /* All the code above this point runs on the coroutine stack.
     201     All the code below this point runs on the main stack.  */
     202  
     203  static int test_status;
     204  static unsigned char *co_stack_buffer;
     205  static size_t co_stack_size;
     206  
     207  static unsigned int
     208  count_test_patterns (unsigned char *buf, size_t bufsiz)
     209  {
     210    VALGRIND_MAKE_MEM_DEFINED (buf, bufsiz);
     211    unsigned char *first = memmem (buf, bufsiz, test_pattern, PATTERN_SIZE);
     212    if (!first)
     213      return 0;
     214    unsigned int cnt = 0;
     215    for (unsigned int i = 0; i < PATTERN_REPS; i++)
     216      {
     217        unsigned char *p = first + i*PATTERN_SIZE;
     218        if (p + PATTERN_SIZE - buf > (ptrdiff_t)bufsiz)
     219          break;
     220        if (memcmp (p, test_pattern, PATTERN_SIZE) == 0)
     221          cnt++;
     222      }
     223    return cnt;
     224  }
     225  
     226  static void
     227  check_test_buffer (enum test_expectation expected,
     228                     const char *label, const char *stage)
     229  {
     230    unsigned int cnt = count_test_patterns (co_stack_buffer, co_stack_size);
     231    switch (expected)
     232      {
     233      case EXPECT_NONE:
     234        if (cnt == 0)
     235          printf ("PASS: %s/%s: expected 0 got %u\n", label, stage, cnt);
     236        else
     237          {
     238            printf ("FAIL: %s/%s: expected 0 got %u\n", label, stage, cnt);
     239            test_status = 1;
     240          }
     241        break;
     242  
     243      case EXPECT_SOME:
     244        if (cnt > 0)
     245          printf ("PASS: %s/%s: expected some got %u\n", label, stage, cnt);
     246        else
     247          {
     248            printf ("FAIL: %s/%s: expected some got 0\n", label, stage);
     249            test_status = 1;
     250          }
     251        break;
     252  
     253      case EXPECT_ALL:
     254        if (cnt == PATTERN_REPS)
     255          printf ("PASS: %s/%s: expected %d got %u\n", label, stage,
     256                  PATTERN_REPS, cnt);
     257        else
     258          {
     259            printf ("FAIL: %s/%s: expected %d got %u\n", label, stage,
     260                    PATTERN_REPS, cnt);
     261            test_status = 1;
     262          }
     263        break;
     264  
     265      case NO_EXPECTATIONS:
     266        printf ("INFO: %s/%s: found %u patterns%s\n", label, stage, cnt,
     267                cnt == 0 ? " (memset not eliminated)" : "");
     268        break;
     269  
     270      default:
     271        printf ("ERROR: %s/%s: invalid value for 'expected' = %d\n",
     272                label, stage, (int)expected);
     273        test_status = 1;
     274      }
     275  }
     276  
     277  static void
     278  test_loop (void)
     279  {
     280    cur_subtest = subtests;
     281    while (cur_subtest->setup_subtest)
     282      {
     283        if (swapcontext (&uc_main, &uc_co))
     284          abort ();
     285        check_test_buffer (EXPECT_ALL, cur_subtest->label, "prepare");
     286        if (swapcontext (&uc_main, &uc_co))
     287          abort ();
     288        check_test_buffer (cur_subtest->expected, cur_subtest->label, "test");
     289        cur_subtest++;
     290      }
     291    /* Terminate the coroutine.  */
     292    if (swapcontext (&uc_main, &uc_co))
     293      abort ();
     294  }
     295  
     296  int
     297  main (void)
     298  {
     299    size_t page_alignment = sizeof (void *);
     300    long page_alignment_l = sysconf (_SC_PAGESIZE);
     301    if (page_alignment_l > (long) sizeof (void *))
     302      page_alignment = (size_t) page_alignment_l;
     303  
     304    co_stack_size = (size_t) SIGSTKSZ + (size_t) TEST_BUFFER_SIZE;
     305    if (co_stack_size < page_alignment * 4)
     306      co_stack_size = page_alignment * 4;
     307  
     308    void *p;
     309    int err = posix_memalign (&p, page_alignment, co_stack_size);
     310    if (err || !p)
     311      {
     312        printf ("ERROR: allocating alt stack: %s\n", strerror (err));
     313        return 2;
     314      }
     315    co_stack_buffer = p;
     316    memset (co_stack_buffer, 0, co_stack_size);
     317    VALGRIND_STACK_REGISTER (co_stack_buffer, co_stack_buffer + co_stack_size);
     318  
     319    if (getcontext (&uc_co))
     320      {
     321        printf ("ERROR: allocating coroutine context: %s\n", strerror (err));
     322        return 2;
     323      }
     324    uc_co.uc_stack.ss_sp   = co_stack_buffer;
     325    uc_co.uc_stack.ss_size = co_stack_size;
     326    uc_co.uc_link          = &uc_main;
     327    makecontext (&uc_co, test_coroutine, 0);
     328  
     329    test_loop ();
     330    return test_status;
     331  }
     332  
     333  #endif /* have ucontext.h */