(root)/
coreutils-9.4/
gnulib-tests/
test-memset_explicit.c
       1  /* Test memset_explicit.
       2     Copyright 2020-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2020.  */
      18  /* Adapted for memset_explicit by Paul Eggert <eggert@cs.ucla.edu>, 2022.  */
      19  
      20  #include <config.h>
      21  
      22  /* Specification.  */
      23  #include <string.h>
      24  
      25  #include "signature.h"
      26  SIGNATURE_CHECK (memset_explicit, void *, (void *, int, size_t));
      27  
      28  #include <limits.h>
      29  #include <stdio.h>
      30  #include <stdint.h>
      31  #include <stdlib.h>
      32  
      33  #include "vma-iter.h"
      34  #include "macros.h"
      35  
      36  #define SECRET "xyzzy1729"
      37  #define SECRET_SIZE 9
      38  
      39  static char zero[SECRET_SIZE] = { 0 };
      40  
      41  /* Enable this to verify that the test is effective.  */
      42  #if 0
      43  # define memset_explicit(a, c, n)  memset (a, c, n)
      44  #endif
      45  
      46  /* =================== Verify operation on static memory =================== */
      47  
      48  static char stbuf[SECRET_SIZE];
      49  
      50  static void
      51  test_static (void)
      52  {
      53    memcpy (stbuf, SECRET, SECRET_SIZE);
      54    memset_explicit (stbuf, 0, SECRET_SIZE);
      55    ASSERT (memcmp (zero, stbuf, SECRET_SIZE) == 0);
      56    for (int i = 1; i <= UCHAR_MAX; i++)
      57      {
      58        char checkbuf[SECRET_SIZE];
      59        memset (checkbuf, i, SECRET_SIZE);
      60        memcpy (stbuf, SECRET, SECRET_SIZE);
      61        memset_explicit (stbuf, i, SECRET_SIZE);
      62        ASSERT (memcmp (checkbuf, stbuf, SECRET_SIZE) == 0);
      63      }
      64  }
      65  
      66  /* =============== Verify operation on heap-allocated memory =============== */
      67  
      68  /* Test whether an address range is mapped in memory.  */
      69  #if VMA_ITERATE_SUPPORTED
      70  
      71  struct locals
      72  {
      73    uintptr_t range_start;
      74    uintptr_t range_end;
      75  };
      76  
      77  static int
      78  vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
      79                        unsigned int flags)
      80  {
      81    struct locals *lp = (struct locals *) data;
      82  
      83    /* Remove from [range_start, range_end) the part at the beginning or at the
      84       end that is covered by [start, end).  */
      85    if (start <= lp->range_start && end > lp->range_start)
      86      lp->range_start = (end < lp->range_end ? end : lp->range_end);
      87    if (start < lp->range_end && end >= lp->range_end)
      88      lp->range_end = (start > lp->range_start ? start : lp->range_start);
      89  
      90    return 0;
      91  }
      92  
      93  static bool
      94  is_range_mapped (uintptr_t range_start, uintptr_t range_end)
      95  {
      96    struct locals l;
      97  
      98    l.range_start = range_start;
      99    l.range_end = range_end;
     100    vma_iterate (vma_iterate_callback, &l);
     101    return l.range_start == l.range_end;
     102  }
     103  
     104  #else
     105  
     106  static bool
     107  is_range_mapped (uintptr_t range_start, uintptr_t range_end)
     108  {
     109    return true;
     110  }
     111  
     112  #endif
     113  
     114  static void
     115  test_heap (void)
     116  {
     117    char *heapbuf = (char *) malloc (SECRET_SIZE);
     118    ASSERT (heapbuf);
     119    uintptr_t volatile addr = (uintptr_t) heapbuf;
     120    memcpy (heapbuf, SECRET, SECRET_SIZE);
     121    memset_explicit (heapbuf, 0, SECRET_SIZE);
     122    free (heapbuf);
     123    heapbuf = (char *) addr;
     124    if (is_range_mapped (addr, addr + SECRET_SIZE))
     125      {
     126        /* some implementation could override freed memory by canaries so
     127           compare against secret */
     128        ASSERT (memcmp (heapbuf, SECRET, SECRET_SIZE) != 0);
     129        printf ("test_heap: address range is still mapped after free().\n");
     130      }
     131    else
     132      printf ("test_heap: address range is unmapped after free().\n");
     133  }
     134  
     135  /* =============== Verify operation on stack-allocated memory =============== */
     136  
     137  /* There are two passes:
     138       1. Put a secret in memory and invoke memset_explicit on it.
     139       2. Verify that the memory has been erased.
     140     Implement them in the same function, so that they access the same memory
     141     range on the stack.  Declare the local scalars to be volatile so they
     142     are not optimized away.  That way, the test verifies that the compiler
     143     does not eliminate a call to memset_explicit, even if data flow analysis
     144     reveals that the stack area is dead at the end of the function.  */
     145  static bool _GL_ATTRIBUTE_NOINLINE
     146  do_secret_stuff (int volatile pass, char *volatile *volatile last_stackbuf)
     147  {
     148    char stackbuf[SECRET_SIZE];
     149    if (pass == 1)
     150      {
     151        memcpy (stackbuf, SECRET, SECRET_SIZE);
     152        memset_explicit (stackbuf, 0, SECRET_SIZE);
     153        *last_stackbuf = stackbuf;
     154        return false;
     155      }
     156    else /* pass == 2 */
     157      {
     158        /* Use *last_stackbuf here, because stackbuf may be allocated at a
     159           different address than *last_stackbuf.  This can happen
     160           when the compiler splits this function into different functions,
     161           one for pass == 1 and one for pass != 1.  */
     162        return memcmp (zero, *last_stackbuf, SECRET_SIZE) != 0;
     163      }
     164  }
     165  
     166  static void
     167  test_stack (void)
     168  {
     169    int count = 0;
     170    int repeat;
     171    char *volatile last_stackbuf;
     172  
     173    for (repeat = 2 * 1000; repeat > 0; repeat--)
     174      {
     175        /* This odd way of writing two consecutive statements
     176             do_secret_stuff (1, &last_stackbuf);
     177             count += do_secret_stuff (2, &last_stackbuf);
     178           ensures that the two do_secret_stuff calls are performed with the same
     179           stack pointer value, on m68k.  */
     180        if ((repeat % 2) == 0)
     181          do_secret_stuff (1, &last_stackbuf);
     182        else
     183          count += do_secret_stuff (2, &last_stackbuf);
     184      }
     185    /* If memset_explicit works, count is near 0.  (It may be > 0 if there were
     186       some asynchronous signal invocations between the two calls of
     187       do_secret_stuff.)
     188       If memset_explicit is optimized away by the compiler, count comes out as
     189       approximately 1000.  */
     190    printf ("test_stack: count = %d\n", count);
     191    ASSERT (count < 50);
     192  }
     193  
     194  /* ========================================================================== */
     195  
     196  int
     197  main ()
     198  {
     199    test_static ();
     200    test_heap ();
     201    test_stack ();
     202  
     203    return 0;
     204  }