diff options
Diffstat (limited to 'gnulib-tests/test-explicit_bzero.c')
-rw-r--r-- | gnulib-tests/test-explicit_bzero.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/gnulib-tests/test-explicit_bzero.c b/gnulib-tests/test-explicit_bzero.c new file mode 100644 index 0000000..8f06d7f --- /dev/null +++ b/gnulib-tests/test-explicit_bzero.c @@ -0,0 +1,198 @@ +/* Test of explicit_bzero() function. + Copyright (C) 2020-2022 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2020. */ + +#include <config.h> + +/* Specification. */ +#include <string.h> + +#include "signature.h" +SIGNATURE_CHECK (explicit_bzero, void, (void *, size_t)); + +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> + +#include "vma-iter.h" +#include "macros.h" + +/* Suppress GCC warning that do_secret_stuff (2) reads uninitialized + local storage. */ +#if 4 < __GNUC__ + (3 <= __GNUC_MINOR__) +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +#define SECRET "xyzzy1729" +#define SECRET_SIZE 9 + +static char zero[SECRET_SIZE] = { 0 }; + +/* Enable this to verify that the test is effective. */ +#if 0 +# define explicit_bzero(a, n) memset (a, '\0', n) +#endif + +/* =================== Verify operation on static memory =================== */ + +static char stbuf[SECRET_SIZE]; + +static void +test_static (void) +{ + memcpy (stbuf, SECRET, SECRET_SIZE); + explicit_bzero (stbuf, SECRET_SIZE); + ASSERT (memcmp (zero, stbuf, SECRET_SIZE) == 0); +} + +/* =============== Verify operation on heap-allocated memory =============== */ + +/* Test whether an address range is mapped in memory. */ +#if VMA_ITERATE_SUPPORTED + +struct locals +{ + uintptr_t range_start; + uintptr_t range_end; +}; + +static int +vma_iterate_callback (void *data, uintptr_t start, uintptr_t end, + unsigned int flags) +{ + struct locals *lp = (struct locals *) data; + + /* Remove from [range_start, range_end) the part at the beginning or at the + end that is covered by [start, end). */ + if (start <= lp->range_start && end > lp->range_start) + lp->range_start = (end < lp->range_end ? end : lp->range_end); + if (start < lp->range_end && end >= lp->range_end) + lp->range_end = (start > lp->range_start ? start : lp->range_start); + + return 0; +} + +static bool +is_range_mapped (uintptr_t range_start, uintptr_t range_end) +{ + struct locals l; + + l.range_start = range_start; + l.range_end = range_end; + vma_iterate (vma_iterate_callback, &l); + return l.range_start == l.range_end; +} + +#else + +static bool +is_range_mapped (uintptr_t range_start, uintptr_t range_end) +{ + return true; +} + +#endif + +static void +test_heap (void) +{ + char *heapbuf = (char *) malloc (SECRET_SIZE); + uintptr_t addr = (uintptr_t) heapbuf; + memcpy (heapbuf, SECRET, SECRET_SIZE); + explicit_bzero (heapbuf, SECRET_SIZE); + free (heapbuf); + if (is_range_mapped (addr, addr + SECRET_SIZE)) + { + /* some implementation could override freed memory by canaries so + compare against secret */ + ASSERT (memcmp (heapbuf, SECRET, SECRET_SIZE) != 0); + printf ("test_heap: address range is still mapped after free().\n"); + } + else + printf ("test_heap: address range is unmapped after free().\n"); +} + +/* =============== Verify operation on stack-allocated memory =============== */ + +/* There are two passes: + 1. Put a secret in memory and invoke explicit_bzero on it. + 2. Verify that the memory has been erased. + Implement them in the same function, so that they access the same memory + range on the stack. That way, the test verifies that the compiler + does not eliminate a call to explicit_bzero, even if data flow analysis + reveals that the stack area is dead at the end of the function. */ +static int _GL_ATTRIBUTE_NOINLINE +do_secret_stuff (volatile int pass) +{ + static char *last_stackbuf; + char stackbuf[SECRET_SIZE]; + if (pass == 1) + { + memcpy (stackbuf, SECRET, SECRET_SIZE); + explicit_bzero (stackbuf, SECRET_SIZE); + last_stackbuf = stackbuf; + return 0; + } + else /* pass == 2 */ + { + /* Use last_stackbuf here, because stackbuf may be allocated at a + different address than last_stackbuf. This can happen + when the compiler splits this function into different functions, + one for pass == 1 and one for pass != 1. */ + return memcmp (zero, last_stackbuf, SECRET_SIZE) != 0; + } +} + +static void +test_stack (void) +{ + int count = 0; + int repeat; + + for (repeat = 2 * 1000; repeat > 0; repeat--) + { + /* This odd way of writing two consecutive statements + do_secret_stuff (1); + count += do_secret_stuff (2); + ensures that the two do_secret_stuff calls are performed with the same + stack pointer value, on m68k. */ + if ((repeat % 2) == 0) + do_secret_stuff (1); + else + count += do_secret_stuff (2); + } + /* If explicit_bzero works, count is near 0. (It may be > 0 if there were + some asynchronous signal invocations between the two calls of + do_secret_stuff.) + If explicit_bzero is optimized away by the compiler, count comes out as + approximately 1000. */ + printf ("test_stack: count = %d\n", count); + ASSERT (count < 50); +} + +/* ========================================================================== */ + +int +main () +{ + test_static (); + test_heap (); + test_stack (); + + return 0; +} |