summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2022-11-27 20:52:04 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2022-11-28 22:03:58 -0800
commite4603ee9bab4d34a3617bbde474ae9041997ec9a (patch)
treea9b44023a8bd50ef6863dac331eb9e4d770a1735 /tests
parentadadac58193206f7ad9c9d66ea0a7c18d5a06158 (diff)
downloadgnulib-e4603ee9bab4d34a3617bbde474ae9041997ec9a.tar.gz
memset_explicit: new module
* doc/posix-functions/memset_explicit.texi, lib/memset_explicit.c: * m4/memset_explicit.m4, modules/memset_explicit: * modules/memset_explicit-tests, tests/test-memset_explicit.c: New files. * lib/string.in.h (memset_explict): New decl. * m4/string_h.m4 (gl_STRING_H, gl_STRING_H_REQUIRE_DEFAULTS) (gl_STRING_H_DEFAULTS): * modules/string (string.h): Support memset_explicit.
Diffstat (limited to 'tests')
-rw-r--r--tests/test-memset_explicit.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/tests/test-memset_explicit.c b/tests/test-memset_explicit.c
new file mode 100644
index 0000000000..3ad343b73f
--- /dev/null
+++ b/tests/test-memset_explicit.c
@@ -0,0 +1,204 @@
+/* Test memset_explicit.
+ Copyright 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. */
+/* Adapted for memset_explicit by Paul Eggert <eggert@cs.ucla.edu>, 2022. */
+
+#include <config.h>
+
+/* Specification. */
+#include <string.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (memset_explicit, void, (void *, int, size_t));
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "vma-iter.h"
+#include "macros.h"
+
+#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 memset_explicit(a, c, n) memset (a, c, n)
+#endif
+
+/* =================== Verify operation on static memory =================== */
+
+static char stbuf[SECRET_SIZE];
+
+static void
+test_static (void)
+{
+ memcpy (stbuf, SECRET, SECRET_SIZE);
+ memset_explicit (stbuf, 0, SECRET_SIZE);
+ ASSERT (memcmp (zero, stbuf, SECRET_SIZE) == 0);
+ for (int i = 1; i <= UCHAR_MAX; i++)
+ {
+ char checkbuf[SECRET_SIZE];
+ memset (checkbuf, i, SECRET_SIZE);
+ memcpy (stbuf, SECRET, SECRET_SIZE);
+ memset_explicit (stbuf, i, SECRET_SIZE);
+ ASSERT (memcmp (checkbuf, 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);
+ ASSERT (heapbuf);
+ uintptr_t volatile addr = (uintptr_t) heapbuf;
+ memcpy (heapbuf, SECRET, SECRET_SIZE);
+ memset_explicit (heapbuf, 0, SECRET_SIZE);
+ free (heapbuf);
+ heapbuf = (char *) addr;
+ 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 memset_explicit 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. Declare the local scalars to be volatile so they
+ are not optimized away. That way, the test verifies that the compiler
+ does not eliminate a call to memset_explicit, even if data flow analysis
+ reveals that the stack area is dead at the end of the function. */
+static bool _GL_ATTRIBUTE_NOINLINE
+do_secret_stuff (int volatile pass, char *volatile *volatile last_stackbuf)
+{
+ char stackbuf[SECRET_SIZE];
+ if (pass == 1)
+ {
+ memcpy (stackbuf, SECRET, SECRET_SIZE);
+ memset_explicit (stackbuf, 0, SECRET_SIZE);
+ *last_stackbuf = stackbuf;
+ return false;
+ }
+ 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;
+ char *volatile last_stackbuf;
+
+ for (repeat = 2 * 1000; repeat > 0; repeat--)
+ {
+ /* This odd way of writing two consecutive statements
+ do_secret_stuff (1, &last_stackbuf);
+ count += do_secret_stuff (2, &last_stackbuf);
+ 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, &last_stackbuf);
+ else
+ count += do_secret_stuff (2, &last_stackbuf);
+ }
+ /* If memset_explicit 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 memset_explicit 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;
+}