string-util: rework memory_erase() so that it cannot be optimized away

memory_erase() so far just called memset(), which the compiler might
optimize away under certain conditions if it feels there's benefit in
it. C11 knows a new memset_s() call that is like memset(), but may not
be optimized away. Ideally, we'd just use that call, but glibc currently
does not support it. Hence, implement our own simplistic version of it.

We use a GCC pragma to turn off optimization for this call, and also use
the "volatile" keyword on the pointers to ensure that gcc will use the
pointers as-is. According to a variety of internet sources, either one
does the trick. However, there are also reports that at least the
volatile thing isn't fully correct, hence let's add some snake oil and
employ both techniques.

https://news.ycombinator.com/item?id=4711346
This commit is contained in:
Lennart Poettering 2015-11-01 20:28:55 +01:00
parent 5732a7dbb0
commit 9fe4ea21be
5 changed files with 94 additions and 10 deletions

1
.gitignore vendored
View file

@ -256,6 +256,7 @@
/test-socket-util
/test-ssd
/test-strbuf
/test-string-util
/test-strip-tab-ansi
/test-strv
/test-strxcpyx

View file

@ -1447,6 +1447,7 @@ tests += \
test-utf8 \
test-ellipsize \
test-util \
test-string-util \
test-extract-word \
test-parse-util \
test-user-util \
@ -1748,6 +1749,12 @@ test_util_SOURCES = \
test_util_LDADD = \
libshared.la
test_string_util_SOURCES = \
src/test/test-string-util.c
test_string_util_LDADD = \
libshared.la
test_extract_word_SOURCES = \
src/test/test-extract-word.c

View file

@ -748,23 +748,38 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
void string_erase(char *x) {
#pragma GCC push_options
#pragma GCC optimize("O0")
void* memory_erase(void *p, size_t l) {
volatile uint8_t* x = (volatile uint8_t*) p;
/* This basically does what memset() does, but hopefully isn't
* optimized away by the compiler. One of those days, when
* glibc learns memset_s() we should replace this call by
* memset_s(), but until then this has to do. */
for (; l > 0; l--)
*(x++) = 'x';
return p;
}
#pragma GCC pop_options
char* string_erase(char *x) {
if (!x)
return;
return NULL;
/* A delicious drop of snake-oil! To be called on memory where
* we stored passphrases or so, after we used them. */
memory_erase(x, strlen(x));
return memory_erase(x, strlen(x));
}
char *string_free_erase(char *s) {
if (!s)
return NULL;
string_erase(s);
return mfree(s);
return mfree(string_erase(s));
}
bool string_is_safe(const char *p) {

View file

@ -162,8 +162,8 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
return memmem(haystack, haystacklen, needle, needlelen);
}
#define memory_erase(p, l) memset((p), 'x', (l))
void string_erase(char *x);
void* memory_erase(void *p, size_t l);
char *string_erase(char *x);
char *string_free_erase(char *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase);

View file

@ -0,0 +1,61 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "string-util.h"
static void test_string_erase(void) {
char *x;
x = strdupa("");
assert_se(streq(string_erase(x), ""));
x = strdupa("1");
assert_se(streq(string_erase(x), "x"));
x = strdupa("12");
assert_se(streq(string_erase(x), "xx"));
x = strdupa("123");
assert_se(streq(string_erase(x), "xxx"));
x = strdupa("1234");
assert_se(streq(string_erase(x), "xxxx"));
x = strdupa("12345");
assert_se(streq(string_erase(x), "xxxxx"));
x = strdupa("123456");
assert_se(streq(string_erase(x), "xxxxxx"));
x = strdupa("1234567");
assert_se(streq(string_erase(x), "xxxxxxx"));
x = strdupa("12345678");
assert_se(streq(string_erase(x), "xxxxxxxx"));
x = strdupa("123456789");
assert_se(streq(string_erase(x), "xxxxxxxxx"));
}
int main(int argc, char *argv[]) {
test_string_erase();
return 0;
}