Skip to content
Snippets Groups Projects
Commit e82e600d authored by Nick Mathewson's avatar Nick Mathewson :game_die:
Browse files

Here is a test for memwipe.

It invokes undefined behavior, I'm afraid, since there's no other
c-legal way to test whether memwipe() works when we're not allowed to
look at it.

Closes ticket 15377.
parent 98c39421
No related branches found
No related tags found
No related merge requests found
o Testing:
- Add a test to verify that the compiler does not eliminate our
memwipe() implementation. Closes ticket 15377.
TESTS += src/test/test src/test/test-slow
TESTS += src/test/test src/test/test-slow src/test/test-memwipe
noinst_PROGRAMS+= src/test/bench
if UNITTESTS_ENABLED
noinst_PROGRAMS+= \
src/test/test \
src/test/test-slow \
src/test/test-memwipe \
src/test/test-child \
src/test/test_workqueue
endif
......@@ -73,6 +74,9 @@ src_test_test_slow_SOURCES = \
src/test/testing_common.c \
src/ext/tinytest.c
src_test_test_memwipe_SOURCES = \
src/test/test-memwipe.c
src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
......@@ -100,6 +104,11 @@ src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
src_test_test_slow_LDADD = $(src_test_test_LDADD)
src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
src_test_test_memwipe_CPPFLAGS = $(src_test_test_CPPFLAGS)
src_test_test_memwipe_CFLAGS = $(src_test_test_CFLAGS)
src_test_test_memwipe_LDADD = $(src_test_test_LDADD)
src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS)
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \
......
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include "crypto.h"
#include "compat.h"
#undef MIN
#define MIN(a,b) ( ((a)<(b)) ? (a) : (b) )
static unsigned fill_a_buffer_memset(void) __attribute__((noinline));
static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline));
static unsigned fill_a_buffer_nothing(void) __attribute__((noinline));
static unsigned fill_heap_buffer_memset(void) __attribute__((noinline));
static unsigned fill_heap_buffer_memwipe(void) __attribute__((noinline));
static unsigned fill_heap_buffer_nothing(void) __attribute__((noinline));
static unsigned check_a_buffer(void) __attribute__((noinline));
const char *s = NULL;
#define FILL_BUFFER_IMPL() \
unsigned int i; \
unsigned sum = 0; \
\
/* Fill up a 1k buffer with a recognizable pattern. */ \
for (i = 0; i < 2048; i += strlen(s)) { \
memcpy(buf+i, s, MIN(strlen(s), 2048-i)); \
} \
\
/* Use the buffer as input to a computation so the above can't get */ \
/* optimized away. */ \
for (i = 0; i < 2048; ++i) { \
sum += (unsigned char)buf[i]; \
}
static unsigned
fill_a_buffer_memset(void)
{
char buf[2048];
FILL_BUFFER_IMPL()
memset(buf, 0, sizeof(buf));
return sum;
}
static unsigned
fill_a_buffer_memwipe(void)
{
char buf[2048];
FILL_BUFFER_IMPL()
memwipe(buf, 0, sizeof(buf));
return sum;
}
static unsigned
fill_a_buffer_nothing(void)
{
char buf[2048];
FILL_BUFFER_IMPL()
return sum;
}
static INLINE int
vmemeq(volatile char *a, const char *b, size_t n)
{
while (n--) {
if (*a++ != *b++)
return 0;
}
return 1;
}
static unsigned
check_a_buffer(void)
{
unsigned int i;
volatile char buf[1024];
unsigned sum = 0;
/* See if this buffer has the string in it.
YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM AN UNINITIALIZED
BUFFER.
If you know a better way to figure out whether the compiler eliminated
the memset/memwipe calls or not, please let me know.
*/
for (i = 0; i < sizeof(buf); ++i) {
if (vmemeq(buf+i, s, strlen(s)))
++sum;
}
return sum;
}
static char *heap_buf = NULL;
static unsigned
fill_heap_buffer_memset(void)
{
char *buf = heap_buf = malloc(2048);
FILL_BUFFER_IMPL()
memset(buf, 0, 2048);
free(buf);
return sum;
}
static unsigned
fill_heap_buffer_memwipe(void)
{
char *buf = heap_buf = malloc(2048);
FILL_BUFFER_IMPL()
memwipe(buf, 0, 2048);
free(buf);
return sum;
}
static unsigned
fill_heap_buffer_nothing(void)
{
char *buf = heap_buf = malloc(2048);
FILL_BUFFER_IMPL()
free(buf);
return sum;
}
static unsigned
check_heap_buffer(void)
{
unsigned int i;
unsigned sum = 0;
volatile char *buf = heap_buf;
/* See if this buffer has the string in it.
YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM A FREED BUFFER.
If you know a better way to figure out whether the compiler eliminated
the memset/memwipe calls or not, please let me know.
*/
for (i = 0; i < sizeof(buf); ++i) {
if (vmemeq(buf+i, s, strlen(s)))
++sum;
}
return sum;
}
static struct testcase {
const char *name;
unsigned (*fill_fn)(void);
unsigned (*check_fn)(void);
} testcases[] = {
{ "nil", fill_a_buffer_nothing, check_a_buffer },
{ "nil-heap", fill_heap_buffer_nothing, check_heap_buffer },
{ "memset", fill_a_buffer_memset, check_a_buffer },
{ "memset-heap", fill_heap_buffer_memset, check_heap_buffer },
{ "memwipe", fill_a_buffer_memwipe, check_a_buffer },
{ "memwipe-heap", fill_heap_buffer_memwipe, check_heap_buffer },
{ NULL, NULL, NULL }
};
int
main(int argc, char **argv)
{
unsigned x, x2;
int i;
int working = 1;
unsigned found[6];
(void) argc; (void) argv;
s = "squamous haberdasher gallimaufry";
memset(found, 0, sizeof(found));
for (i = 0; testcases[i].name; ++i) {
x = testcases[i].fill_fn();
found[i] = testcases[i].check_fn();
x2 = fill_a_buffer_nothing();
if (x != x2) {
working = 0;
}
}
if (!working || !found[0] || !found[1]) {
printf("It appears that this test case may not give you reliable "
"information. Sorry.\n");
}
if (!found[2] && !found[3]) {
printf("It appears that memset is good enough on this platform. Good.\n");
}
if (found[4] || found[5]) {
printf("ERROR: memwipe does not wipe data!\n");
return 1;
} else {
printf("OKAY: memwipe seems to work.");
return 0;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment