/* Test of explicit_bzero() function. Copyright (C) 2020 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 . */ /* Written by Bruno Haible , 2020. */ #include /* Specification. */ #include #include "signature.h" SIGNATURE_CHECK (explicit_bzero, void, (void *, size_t)); #include #include #include #include #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 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. */ static int _GL_ATTRIBUTE_NOINLINE do_secret_stuff (volatile int pass) { char stackbuf[SECRET_SIZE]; if (pass == 1) { memcpy (stackbuf, SECRET, SECRET_SIZE); explicit_bzero (stackbuf, SECRET_SIZE); return 0; } else /* pass == 2 */ { return memcmp (zero, stackbuf, SECRET_SIZE) != 0; } } static void test_stack (void) { int count = 0; int repeat; for (repeat = 1000; repeat > 0; repeat--) { do_secret_stuff (1); 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; }