summaryrefslogtreecommitdiffstats
path: root/gnulib-tests/test-explicit_bzero.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnulib-tests/test-explicit_bzero.c')
-rw-r--r--gnulib-tests/test-explicit_bzero.c198
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;
+}