(root)/
gettext-0.22.4/
gettext-tools/
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  /* Suppress GCC 13.2.1 false alarm, as this test needs a dangling pointer.  */
      47  #if 12 <= __GNUC__
      48  # pragma GCC diagnostic ignored "-Wdangling-pointer"
      49  #endif
      50  
      51  /* =================== Verify operation on static memory =================== */
      52  
      53  static char stbuf[SECRET_SIZE];
      54  
      55  static void
      56  test_static (void)
      57  {
      58    memcpy (stbuf, SECRET, SECRET_SIZE);
      59    memset_explicit (stbuf, 0, SECRET_SIZE);
      60    ASSERT (memcmp (zero, stbuf, SECRET_SIZE) == 0);
      61    for (int i = 1; i <= UCHAR_MAX; i++)
      62      {
      63        char checkbuf[SECRET_SIZE];
      64        memset (checkbuf, i, SECRET_SIZE);
      65        memcpy (stbuf, SECRET, SECRET_SIZE);
      66        memset_explicit (stbuf, i, SECRET_SIZE);
      67        ASSERT (memcmp (checkbuf, stbuf, SECRET_SIZE) == 0);
      68      }
      69  }
      70  
      71  /* =============== Verify operation on heap-allocated memory =============== */
      72  
      73  /* Test whether an address range is mapped in memory.  */
      74  #if VMA_ITERATE_SUPPORTED
      75  
      76  struct locals
      77  {
      78    uintptr_t range_start;
      79    uintptr_t range_end;
      80  };
      81  
      82  static int
      83  vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
      84                        unsigned int flags)
      85  {
      86    struct locals *lp = (struct locals *) data;
      87  
      88    /* Remove from [range_start, range_end) the part at the beginning or at the
      89       end that is covered by [start, end).  */
      90    if (start <= lp->range_start && end > lp->range_start)
      91      lp->range_start = (end < lp->range_end ? end : lp->range_end);
      92    if (start < lp->range_end && end >= lp->range_end)
      93      lp->range_end = (start > lp->range_start ? start : lp->range_start);
      94  
      95    return 0;
      96  }
      97  
      98  static bool
      99  is_range_mapped (uintptr_t range_start, uintptr_t range_end)
     100  {
     101    struct locals l;
     102  
     103    l.range_start = range_start;
     104    l.range_end = range_end;
     105    vma_iterate (vma_iterate_callback, &l);
     106    return l.range_start == l.range_end;
     107  }
     108  
     109  #else
     110  
     111  static bool
     112  is_range_mapped (uintptr_t range_start, uintptr_t range_end)
     113  {
     114    return true;
     115  }
     116  
     117  #endif
     118  
     119  static void
     120  test_heap (void)
     121  {
     122    char *heapbuf = (char *) malloc (SECRET_SIZE);
     123    ASSERT (heapbuf);
     124    uintptr_t volatile addr = (uintptr_t) heapbuf;
     125    memcpy (heapbuf, SECRET, SECRET_SIZE);
     126    memset_explicit (heapbuf, 0, SECRET_SIZE);
     127    free (heapbuf);
     128    heapbuf = (char *) addr;
     129    if (is_range_mapped (addr, addr + SECRET_SIZE))
     130      {
     131        /* some implementation could override freed memory by canaries so
     132           compare against secret */
     133        ASSERT (memcmp (heapbuf, SECRET, SECRET_SIZE) != 0);
     134        printf ("test_heap: address range is still mapped after free().\n");
     135      }
     136    else
     137      printf ("test_heap: address range is unmapped after free().\n");
     138  }
     139  
     140  /* =============== Verify operation on stack-allocated memory =============== */
     141  
     142  /* There are two passes:
     143       1. Put a secret in memory and invoke memset_explicit on it.
     144       2. Verify that the memory has been erased.
     145     Implement them in the same function, so that they access the same memory
     146     range on the stack.  Declare the local scalars to be volatile so they
     147     are not optimized away.  That way, the test verifies that the compiler
     148     does not eliminate a call to memset_explicit, even if data flow analysis
     149     reveals that the stack area is dead at the end of the function.  */
     150  static bool _GL_ATTRIBUTE_NOINLINE
     151  #if __GNUC__ + (__GNUC_MINOR__ >= 5) > 4
     152  __attribute__ ((__noclone__))
     153  #endif
     154  #if __GNUC__ >= 8
     155  __attribute__ ((__noipa__))
     156  #endif
     157  do_secret_stuff (int volatile pass, char *volatile *volatile last_stackbuf)
     158  {
     159    char stackbuf[SECRET_SIZE];
     160    if (pass == 1)
     161      {
     162        memcpy (stackbuf, SECRET, SECRET_SIZE);
     163        memset_explicit (stackbuf, 0, SECRET_SIZE);
     164        *last_stackbuf = stackbuf;
     165        return false;
     166      }
     167    else /* pass == 2 */
     168      {
     169        /* Use *last_stackbuf here, because stackbuf may be allocated at a
     170           different address than *last_stackbuf.  This can happen
     171           when the compiler splits this function into different functions,
     172           one for pass == 1 and one for pass != 1.  */
     173        return memcmp (zero, *last_stackbuf, SECRET_SIZE) != 0;
     174      }
     175  }
     176  
     177  static void
     178  test_stack (void)
     179  {
     180    int count = 0;
     181    int repeat;
     182    char *volatile last_stackbuf;
     183  
     184    for (repeat = 2 * 1000; repeat > 0; repeat--)
     185      {
     186        /* This odd way of writing two consecutive statements
     187             do_secret_stuff (1, &last_stackbuf);
     188             count += do_secret_stuff (2, &last_stackbuf);
     189           ensures that the two do_secret_stuff calls are performed with the same
     190           stack pointer value, on m68k.  */
     191        if ((repeat % 2) == 0)
     192          do_secret_stuff (1, &last_stackbuf);
     193        else
     194          count += do_secret_stuff (2, &last_stackbuf);
     195      }
     196    /* If memset_explicit works, count is near 0.  (It may be > 0 if there were
     197       some asynchronous signal invocations between the two calls of
     198       do_secret_stuff.)
     199       If memset_explicit is optimized away by the compiler, count comes out as
     200       approximately 1000.  */
     201    printf ("test_stack: count = %d\n", count);
     202    ASSERT (count < 50);
     203  }
     204  
     205  /* ========================================================================== */
     206  
     207  int
     208  main ()
     209  {
     210    test_static ();
     211    test_heap ();
     212    test_stack ();
     213  
     214    return 0;
     215  }