[1/3] New string function explicit_bzero (from OpenBSD).
Commit Message
explicit_bzero(s, n) is the same as memset(s, 0, n), except that the
compiler is not allowed to delete a call to explicit_bzero even if the
memory pointed to by 's' is dead after the call. We achieve this effect
by defining it to call memset() and then a second function,
extern void __glibc_read_memory (const void *, size_t)
__attribute_noinline__;
which does nothing -- but the compiler is prevented from knowing that
it does nothing, and so the pointer "escapes" and the memory is not
treated as dead. (Concretely, __glibc_read_memory is forced
out-of-line, defined in a file containing nothing else, and comments
in both string/read_memory.c and string/Makefile document that it must
not be subject to link-time optimization.)
There are two new tests: test-explicit_bzero.c verifies the
visible semantics in the same way as the existing test-bzero.c,
and tst-xbzero-opt.c verifies the not-being-optimized-out property.
The latter is conceptually based on a test written by Matthew Dempsky
for the OpenBSD regression suite.
* string/explicit_bzero.c, string/read_memory.c: New routines.
* string/test-explicit_bzero.c, string/tst-xbzero-opt.c: New tests.
* string/Makefile (routines, strop-tests, tests): Add them.
* string/test-memset.c: Add ifdeffage for testing explicit_bzero.
* string/string.h [__USE_MISC]: Declare explicit_bzero and
__glibc_read_memory.
* include/string.h: Declare __internal_glibc_read_memory,
__explicit_bzero, and __internal_explicit_bzero.
* manual/string.texi: Document explicit_bzero.
* string/Versions [GLIBC_2.25]: Add explicit_bzero,
__explicit_bzero, and __glibc_read_memory.
* sysdeps/arm/nacl/libc.abilist
* sysdeps/unix/sysv/linux/aarch64/libc.abilist
* sysdeps/unix/sysv/linux/alpha/libc.abilist
* sysdeps/unix/sysv/linux/arm/libc.abilist
* sysdeps/unix/sysv/linux/hppa/libc.abilist
* sysdeps/unix/sysv/linux/i386/libc.abilist
* sysdeps/unix/sysv/linux/ia64/libc.abilist
* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
* sysdeps/unix/sysv/linux/microblaze/libc.abilist
* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
* sysdeps/unix/sysv/linux/nios2/libc.abilist
* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
* sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
* sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
* sysdeps/unix/sysv/linux/sh/libc.abilist
* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
* sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
* sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
* sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist:
Add entries for explicit_bzero, __explicit_bzero,
and __glibc_read_memory.
---
include/string.h | 12 +
manual/string.texi | 124 ++++++++
string/Makefile | 12 +-
string/Versions | 10 +
string/explicit_bzero.c | 33 ++
string/read_memory.c | 41 +++
string/string.h | 9 +
string/test-explicit_bzero.c | 20 ++
string/test-memset.c | 10 +-
string/tst-xbzero-opt.c | 348 +++++++++++++++++++++
sysdeps/arm/nacl/libc.abilist | 3 +
sysdeps/unix/sysv/linux/aarch64/libc.abilist | 3 +
sysdeps/unix/sysv/linux/alpha/libc.abilist | 3 +
sysdeps/unix/sysv/linux/arm/libc.abilist | 3 +
sysdeps/unix/sysv/linux/hppa/libc.abilist | 3 +
sysdeps/unix/sysv/linux/i386/libc.abilist | 3 +
sysdeps/unix/sysv/linux/ia64/libc.abilist | 3 +
sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist | 3 +
sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist | 3 +
sysdeps/unix/sysv/linux/microblaze/libc.abilist | 3 +
.../unix/sysv/linux/mips/mips32/fpu/libc.abilist | 3 +
.../unix/sysv/linux/mips/mips32/nofpu/libc.abilist | 3 +
.../unix/sysv/linux/mips/mips64/n32/libc.abilist | 3 +
.../unix/sysv/linux/mips/mips64/n64/libc.abilist | 3 +
sysdeps/unix/sysv/linux/nios2/libc.abilist | 3 +
.../sysv/linux/powerpc/powerpc32/fpu/libc.abilist | 3 +
.../linux/powerpc/powerpc32/nofpu/libc.abilist | 3 +
.../sysv/linux/powerpc/powerpc64/libc-le.abilist | 3 +
.../unix/sysv/linux/powerpc/powerpc64/libc.abilist | 3 +
sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist | 3 +
sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist | 3 +
sysdeps/unix/sysv/linux/sh/libc.abilist | 3 +
sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist | 3 +
sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist | 3 +
.../sysv/linux/tile/tilegx/tilegx32/libc.abilist | 3 +
.../sysv/linux/tile/tilegx/tilegx64/libc.abilist | 3 +
sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist | 3 +
sysdeps/unix/sysv/linux/x86_64/64/libc.abilist | 3 +
sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist | 3 +
39 files changed, 702 insertions(+), 4 deletions(-)
create mode 100644 string/explicit_bzero.c
create mode 100644 string/read_memory.c
create mode 100644 string/test-explicit_bzero.c
create mode 100644 string/tst-xbzero-opt.c
Comments
On 11/15/2016 04:55 PM, Zack Weinberg wrote:
> explicit_bzero(s, n) is the same as memset(s, 0, n), except that the
> compiler is not allowed to delete a call to explicit_bzero even if the
> memory pointed to by 's' is dead after the call. We achieve this effect
> by defining it to call memset() and then a second function,
>
> extern void __glibc_read_memory (const void *, size_t)
> __attribute_noinline__;
>
> which does nothing -- but the compiler is prevented from knowing that
> it does nothing, and so the pointer "escapes" and the memory is not
> treated as dead. (Concretely, __glibc_read_memory is forced
> out-of-line, defined in a file containing nothing else, and comments
> in both string/read_memory.c and string/Makefile document that it must
> not be subject to link-time optimization.)
>
> There are two new tests: test-explicit_bzero.c verifies the
> visible semantics in the same way as the existing test-bzero.c,
> and tst-xbzero-opt.c verifies the not-being-optimized-out property.
> The latter is conceptually based on a test written by Matthew Dempsky
> for the OpenBSD regression suite.
[...]
> diff --git a/manual/string.texi b/manual/string.texi
> index 1986357..3e1bf8d 100644
> --- a/manual/string.texi
> +++ b/manual/string.texi
> @@ -34,6 +34,8 @@ too.
> * Search Functions:: Searching for a specific element or substring.
> * Finding Tokens in a String:: Splitting a string into tokens by looking
> for delimiters.
> +* Erasing Sensitive Data:: Clearing memory which contains sensitive
> + data, after it's no longer needed.
> * strfry:: Function for flash-cooking a string.
> * Trivial Encryption:: Obscuring data.
> * Encode Binary Data:: Encoding and Decoding of Binary Data.
> @@ -2404,6 +2406,128 @@ contains no '/' bytes, then "." is returned. The prototype for this
> function can be found in @file{libgen.h}.
> @end deftypefun
>
> +@node Erasing Sensitive Data
> +@section Erasing Sensitive Data
> +
> +It is sometimes necessary to make sure that data in memory is erased
> +after use, even if no correct C program could access it again. For
> +instance, a cryptographic key should not be left in memory after the
> +program is finished using it, because there might be a bug that causes
> +junk data, including the key, to be revealed to the outside world.
> +
> +However, the C compiler knows that no correct program can access data
> +after it's deallocated, so it may delete ``unnecessary'' stores that
> +erase data just before deallocating it. @code{memset}, @code{bzero},
> +and a manually-written loop are all equally unreliable, and
> +@code{volatile} will not help.
> +
> +For this situation, @theglibc{} provides @code{explicit_bzero}. It is
> +functionally identical to @code{bzero}, but the C compiler will not
> +treat any use of it as unnecessary.
> +
> +@comment string.h
> +@comment BSD
> +@deftypefun void explicit_bzero (void *@var{block}, size_t @var{len})
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +
> +@code{explicit_bzero} writes zero into each of the first @var{len}
> +bytes of the object beginning at @var{block},
I find that wording ("first len bytes") a little confusing. How about
[[
@code{explicit_bzero} writes zero into each of the @var{len}
bytes starting the location pointed to by @var{block},
]]
> just as @code{bzero}
> +would. The zeroes are always written, even if the object is about to
> +be deallocated. (Variables on the stack are deallocated when they go
> +out of scope; heap blocks created by @code{malloc} are deallocated
> +when passed to @code{free}.)
> +
> +@smallexample
> +@group
> +#include <string.h>
> +
> +extern void encrypt (const char *key, const char *in,
> + char *out, size_t n);
> +extern void genkey (const char *phrase, char *key);
> +
> +void encrypt_with_phrase (const char *phrase, const char *in,
> + char *out, size_t n)
> +@{
> + char key[16];
> + genkey (phrase, key);
> + encrypt (key, in, out, n);
> + explicit_bzero (key, 16);
> +@}
> +@end group
> +@end smallexample
> +
> +@noindent
> +If @code{bzero} or @code{memset} had been used in this function, the C
> +compiler might remove it as unnecessary, but it will not do this with
> +@code{explicit_bzero}.
> +
> +@strong{Warning:} The @emph{only} optimization disabled by
s/@strong{Warning:}/Note:/ ?
(I mean, why does this need to be a _warning_?
> +@code{explicit_bzero} is removal of ``unnecessary'' writes to memory.
Also, I would rewrite that first sentence as:
[[
Note: The @emph{only} optimization that @code{explicit_bzero} disables
is removal of ``unnecessary'' writes to memory.
]]
I find that phrasing a little easier to parse.
> +In all other respects, the compiler is allowed to optimize as it would
> +for @code{memset}. For instance, it may replace the function call
> +with inline memory writes, and it may deduce that @var{block} cannot
> +be a null pointer.
> +
> +@strong{Warning:} The compiler may make copies of any object, or parts
Maybe make that "Warning" a "Note". Making it a warning tends to scare
the reader about explicit_bzero(), when really this is an issue
that is caused by the compiler.
> +of it, in temporary storage areas, such as registers and ``scratch''
> +stack space. @code{explicit_bzero} does not erase copies of sensitive
> +data. At present, there is no way to prevent temporary copies from
> +being made, nor to arrange for them to be erased. Declaring sensitive
> +variables as @code{volatile} will make the problem @emph{worse}; the
> +compiler needs to make @emph{more} copies of @code{volatile} data in
> +order to operate on it correctly.
> +
> +@strong{Warning:} In some situations, using @code{explicit_bzero} will
So, I think this one really does deserve to be a Warning...
By the way, the name explicit_bzero() does seem odd. "Explicit"
isn't the right word... But I guess we are stuck with what OpenBSD
gave us.
Cheers,
Michael
> +@emph{cause} creation of an additional copy of sensitive data, and
> +only that copy will be cleared:
> +
> +@smallexample
> +@group
> +#include <string.h>
> +
> +struct key
> +@{
> + unsigned long long low;
> + unsigned long long high;
> +@};
> +
> +struct key get_key (void);
> +void use_key (struct key);
> +
> +void
> +with_clear (void)
> +@{
> + struct key k;
> + k = get_key ();
> + use_key (k);
> + explicit_bzero (&k, sizeof (k));
> +@}
> +@end group
> +@end smallexample
> +
> +@noindent
> +Without the call to @code{explicit_bzero}, @var{k} might not need to
> +be stored in memory: perhaps its value could be returned from
> +@code{get_key} and passed to @code{use_key} using only CPU registers.
> +@code{explicit_bzero} operates on memory, so the compiler has to make
> +a copy of @var{k} in memory for it, and the original in the CPU
> +registers remains intact. This can occur for any variable whose
> +address is only taken in a call to @code{explicit_bzero}, even if it
> +might seem ``too large'' to be stored in registers.
> +
> +There is currently no way to avoid this problem. @Theglibc{}'s
> +implementation of @code{explicit_bzero} contains a hack that can
> +sometimes prevent the sensitive data from being copied into memory,
> +but it is not guaranteed to work, and the original in the CPU
> +registers is unaffected. Again, declaring sensitive variables as
> +@code{volatile} will make the problem worse.
> +
> +@strong{Portability Note:} This function first appeared in OpenBSD 5.5
> +and has not been standardized. @Theglibc{} declares this function in
> +@file{string.h}, but on other systems it may be in @file{strings.h}
> +instead.
> +@end deftypefun
> +
> @node strfry
> @section strfry
>
> diff --git a/string/Makefile b/string/Makefile
> index 69d3f80..0b6486e 100644
> --- a/string/Makefile
> +++ b/string/Makefile
> @@ -41,20 +41,26 @@ routines := strcat strchr strcmp strcoll strcpy strcspn \
> addsep replace) \
> envz basename \
> strcoll_l strxfrm_l string-inlines memrchr \
> - xpg-strerror strerror_l
> + xpg-strerror strerror_l explicit_bzero
> +
> +# Attention future hackers trying to enable link-time optimization for
> +# glibc: this file *must not* be subject to LTO. It is added separately
> +# to 'routines' to document this. See comments in this file for details.
> +routines += read_memory
>
> strop-tests := memchr memcmp memcpy memmove mempcpy memset memccpy \
> stpcpy stpncpy strcat strchr strcmp strcpy strcspn \
> strlen strncmp strncpy strpbrk strrchr strspn memmem \
> strstr strcasestr strnlen strcasecmp strncasecmp \
> - strncat rawmemchr strchrnul bcopy bzero memrchr
> + strncat rawmemchr strchrnul bcopy bzero memrchr \
> + explicit_bzero
> tests := tester inl-tester noinl-tester testcopy test-ffs \
> tst-strlen stratcliff tst-svc tst-inlcall \
> bug-strncat1 bug-strspn1 bug-strpbrk1 tst-bswap \
> tst-strtok tst-strxfrm bug-strcoll1 tst-strfry \
> bug-strtok1 $(addprefix test-,$(strop-tests)) \
> bug-envz1 tst-strxfrm2 tst-endian tst-svc2 \
> - tst-strtok_r bug-strcoll2 tst-cmp
> + tst-strtok_r bug-strcoll2 tst-cmp tst-xbzero-opt
>
> xtests = tst-strcoll-overflow
>
> diff --git a/string/Versions b/string/Versions
> index 475c1fd..4f434f3 100644
> --- a/string/Versions
> +++ b/string/Versions
> @@ -82,4 +82,14 @@ libc {
> }
> GLIBC_2.24 {
> }
> + GLIBC_2.25 {
> + # used by inlines in bits/string2.h and bits/string3.h
> + __glibc_read_memory;
> +
> + # used by libcrypt
> + __explicit_bzero;
> +
> + # e*
> + explicit_bzero;
> + }
> }
> diff --git a/string/explicit_bzero.c b/string/explicit_bzero.c
> new file mode 100644
> index 0000000..6c3dc9a
> --- /dev/null
> +++ b/string/explicit_bzero.c
> @@ -0,0 +1,33 @@
> +/* Copyright (C) 2016 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library 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.
> +
> + The GNU C Library 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 the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <features.h>
> +#undef __USE_STRING_INLINES
> +#define __NO_STRING_INLINES
> +#include <string.h>
> +
> +/* Set LEN bytes of S to 0. The compiler will not delete a call to
> + this function, even if S is dead after the call. */
> +void
> +__internal_explicit_bzero (void *s, size_t len)
> +{
> + memset (s, '\0', len);
> + __internal_glibc_read_memory (s, len);
> +}
> +libc_hidden_def (__internal_explicit_bzero)
> +strong_alias (__internal_explicit_bzero, __explicit_bzero)
> +weak_alias (__internal_explicit_bzero, explicit_bzero)
> diff --git a/string/read_memory.c b/string/read_memory.c
> new file mode 100644
> index 0000000..c4a5990
> --- /dev/null
> +++ b/string/read_memory.c
> @@ -0,0 +1,41 @@
> +/* Copyright (C) 2016 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library 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.
> +
> + The GNU C Library 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 the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +#include <string.h>
> +
> +/* This function is an optimization fence. It doesn't do anything
> + itself, but calls to it prevent calls to explicit_bzero from being
> + optimized away. In order to achieve this effect, this function
> + must never, under any circumstances, be inlined or subjected to
> + inter-procedural optimization. string.h declares this function
> + with attributes that, in conjunction with the no-op asm insert, are
> + sufficient to prevent problems in the current (2016) generation of
> + compilers, but *only if* this file is *not* compiled with -flto.
> + At present, this is not an issue since glibc is never compiled with
> + -flto, but should that ever change, this file must be excepted.
> +
> + The 'volatile' below is technically not necessary but is included
> + for explicitness. */
> +
> +void
> +internal_function
> +__internal_glibc_read_memory(const void *s, size_t len)
> +{
> + asm volatile ("");
> +}
> +libc_hidden_def (__internal_glibc_read_memory)
> +strong_alias (__internal_glibc_read_memory, __glibc_read_memory)
> diff --git a/string/string.h b/string/string.h
> index 57deaa4..682661a 100644
> --- a/string/string.h
> +++ b/string/string.h
> @@ -455,6 +455,15 @@ extern void bcopy (const void *__src, void *__dest, size_t __n)
> /* Set N bytes of S to 0. */
> extern void bzero (void *__s, size_t __n) __THROW __nonnull ((1));
>
> +/* As bzero, but the compiler will not delete a call to this
> + function, even if S is dead after the call. */
> +extern void explicit_bzero (void *__s, size_t __n) __THROW __nonnull ((1));
> +
> +/* Optimization fence, used by bits/string2.h and bits/string3.h
> + inline versions of explicit_bzero. */
> +extern void __glibc_read_memory (const void *__s, size_t __n)
> + __THROW __nonnull ((1)) __attribute_noinline__;
> +
> /* Compare N bytes of S1 and S2 (same as memcmp). */
> extern int bcmp (const void *__s1, const void *__s2, size_t __n)
> __THROW __attribute_pure__ __nonnull ((1, 2));
> diff --git a/string/test-explicit_bzero.c b/string/test-explicit_bzero.c
> new file mode 100644
> index 0000000..5a4543b
> --- /dev/null
> +++ b/string/test-explicit_bzero.c
> @@ -0,0 +1,20 @@
> +/* Test and measure explicit_bzero.
> + Copyright (C) 2016 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library 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.
> +
> + The GNU C Library 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 the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +#define TEST_EXPLICIT_BZERO
> +#define TEST_BZERO
> +#include "test-memset.c"
> diff --git a/string/test-memset.c b/string/test-memset.c
> index fee3bdf..7ca4f20 100644
> --- a/string/test-memset.c
> +++ b/string/test-memset.c
> @@ -19,7 +19,11 @@
>
> #define TEST_MAIN
> #ifdef TEST_BZERO
> -# define TEST_NAME "bzero"
> +# ifdef TEST_EXPLICIT_BZERO
> +# define TEST_NAME "explicit_bzero"
> +# else
> +# define TEST_NAME "bzero"
> +# endif
> #else
> # ifndef WIDE
> # define TEST_NAME "memset"
> @@ -56,7 +60,11 @@ void builtin_bzero (char *, size_t);
>
> IMPL (simple_bzero, 0)
> IMPL (builtin_bzero, 0)
> +#ifdef TEST_EXPLICIT_BZERO
> +IMPL (explicit_bzero, 1)
> +#else
> IMPL (bzero, 1)
> +#endif
>
> void
> simple_bzero (char *s, size_t n)
> diff --git a/string/tst-xbzero-opt.c b/string/tst-xbzero-opt.c
> new file mode 100644
> index 0000000..da35669
> --- /dev/null
> +++ b/string/tst-xbzero-opt.c
> @@ -0,0 +1,348 @@
> +/* Test that explicit_bzero block clears are not optimized out.
> + Copyright (C) 2016 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library 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.
> +
> + The GNU C Library 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 the GNU C Library; if not, see
> + <http://www.gnu.org/licenses/>. */
> +
> +/* This test is conceptually based on a test designed by Matthew
> + Dempsky for the OpenBSD regression suite:
> + <openbsd>/src/regress/lib/libc/explicit_bzero/explicit_bzero.c.
> + The basic idea is, we have a function that contains a
> + block-clearing operation (not necessarily explicit_bzero), after
> + which the block is dead, in the compiler-jargon sense. Execute
> + that function from a signal handler running on an alternative
> + signal stack. Then we have another pointer to the memory region
> + affected by the block clear -- namely, the sigaltstack buffer --
> + and can find out whether it actually happened.
> +
> + The OpenBSD test cautions that some operating systems (e.g. Solaris
> + and OSX) wipe the signal stack upon returning to the normal stack,
> + so the test has to happen while still executing on the signal
> + stack. This, of course, means that the buffer might be clobbered
> + by normal stack operations after the function with the block clear
> + returns (it has to return, so that the block is truly dead). The
> + most straightforward way to deal with this is to have a large block
> + containing several copies of a byte pattern that is unlikely to
> + occur by chance, and check whether _any_ of them survives. */
> +
> +#define _GNU_SOURCE 1
> +
> +#include <errno.h>
> +#include <signal.h>
> +#include <stdarg.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +/* test-skeleton.c unconditionally sets stdout to be unbuffered.
> + vfprintf allocates a great deal of memory on the stack if called
> + with an unbuffered FILE*, overflowing the alt-stack. do_test
> + therefore resets stdout to fully buffered, and we use this wrapper
> + exclusively for output. */
> +static void
> +xprintf (const char *msg, ...)
> +{
> + va_list ap;
> + va_start (ap, msg);
> + vfprintf (stdout, msg, ap);
> + va_end (ap);
> + fflush (stdout);
> +}
> +#define printf dont_use_printf_in_this_file
> +
> +/* The "byte pattern that is unlikely to occur by chance": the first
> + 16 prime numbers (OEIS A000040). */
> +static const unsigned char test_pattern[16] =
> +{
> + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53
> +};
> +
> +#define PATTERN_SIZE (sizeof test_pattern)
> +#define PATTERN_REPS 32
> +#define TEST_BUFFER_SIZE (PATTERN_SIZE * PATTERN_REPS)
> +
> +static void
> +fill_with_test_pattern (unsigned char *buf)
> +{
> + for (unsigned int i = 0; i < PATTERN_REPS; i++)
> + memcpy (buf + i*PATTERN_SIZE, test_pattern, PATTERN_SIZE);
> +}
> +
> +static unsigned int
> +count_test_patterns (unsigned char *buf, size_t bufsiz)
> +{
> + unsigned char *first = memmem (buf, bufsiz, test_pattern, PATTERN_SIZE);
> + if (!first)
> + return 0;
> + unsigned int cnt = 0;
> + for (unsigned int i = 0; i < PATTERN_REPS; i++)
> + {
> + unsigned char *p = first + i*PATTERN_SIZE;
> + if (p + PATTERN_SIZE - buf > bufsiz)
> + break;
> + if (memcmp (p, test_pattern, PATTERN_SIZE) == 0)
> + cnt++;
> + }
> + return cnt;
> +}
> +
> +/* Global test state. */
> +static int failed_subtests;
> +static bool this_subtest_failed;
> +
> +/* The signal stack is allocated with memalign. */
> +static unsigned char *signal_stack_buffer;
> +#define SIGNAL_STACK_SIZE (SIGSTKSZ + TEST_BUFFER_SIZE)
> +
> +enum test_expectation { EXPECT_NONE, EXPECT_SOME, EXPECT_ALL };
> +
> +static void
> +check_test_buffer (enum test_expectation expected,
> + const char *label, const char *stage)
> +{
> + unsigned int cnt = count_test_patterns (signal_stack_buffer,
> + SIGNAL_STACK_SIZE);
> + switch (expected)
> + {
> + case EXPECT_NONE:
> + if (cnt == 0)
> + xprintf ("PASS: %s/%s: expected 0 got %d\n", label, stage, cnt);
> + else
> + {
> + xprintf ("FAIL: %s/%s: expected 0 got %d\n", label, stage, cnt);
> + this_subtest_failed = true;
> + failed_subtests++;
> + }
> + break;
> +
> + case EXPECT_SOME:
> + if (cnt > 0)
> + xprintf ("PASS: %s/%s: expected some got %d\n", label, stage, cnt);
> + else
> + {
> + xprintf ("FAIL: %s/%s: expected some got 0\n", label, stage);
> + this_subtest_failed = true;
> + failed_subtests++;
> + }
> + break;
> +
> + case EXPECT_ALL:
> + if (cnt == PATTERN_REPS)
> + xprintf ("PASS: %s/%s: expected %d got %d\n", label, stage,
> + PATTERN_REPS, cnt);
> + else
> + {
> + xprintf ("FAIL: %s/%s: expected %d got %d\n", label, stage,
> + PATTERN_REPS, cnt);
> + this_subtest_failed = true;
> + failed_subtests++;
> + }
> + break;
> +
> + default:
> + xprintf ("ERROR: %s/%s: invalid value for 'expected' = %d\n",
> + label, stage, (int)expected);
> + this_subtest_failed = true;
> + failed_subtests++;
> + }
> +}
> +
> +/* Always check the test buffer immediately after filling it; this
> + makes externally visible side effects depend on the buffer existing
> + and having been filled in. */
> +static void
> +prepare_test_buffer (unsigned char *buf, const char *label)
> +{
> + fill_with_test_pattern (buf);
> + check_test_buffer (EXPECT_ALL, label, "prepare");
> +
> + unsigned char *loc = memmem (signal_stack_buffer, SIGNAL_STACK_SIZE,
> + test_pattern, PATTERN_SIZE);
> + if (loc == buf)
> + xprintf ("PASS: %s/prepare: expected buffer location %p got %p\n",
> + label, buf, loc);
> + else
> + {
> + xprintf ("FAIL: %s/prepare: expected buffer location %p got %p\n",
> + label, buf, loc);
> + this_subtest_failed = true;
> + failed_subtests++;
> + }
> +}
> +
> +/* There are three subtests, two of which are sanity checks.
> +
> + In the "no_clear" case, we don't do anything to the test buffer
> + between preparing it and letting it go out of scope, and we expect
> + to find it. This confirms that the test buffer does get filled in
> + and we can find it from the stack buffer. In the "ordinary_clear"
> + case, we clear it using memset, and we expect to find it. This
> + confirms that the compiler can optimize out block clears in this
> + context; if it can't, the real test might be succeeding for the
> + wrong reason. Finally, the "explicit_clear" case uses
> + explicit_bzero and expects _not_ to find the test buffer, which is
> + the real test. */
> +
> +static void
> +setup_no_clear (void)
> +{
> + unsigned char buf[TEST_BUFFER_SIZE];
> + prepare_test_buffer (buf, "no clear");
> +}
> +
> +static void
> +setup_ordinary_clear (void)
> +{
> + unsigned char buf[TEST_BUFFER_SIZE];
> + prepare_test_buffer (buf, "ordinary clear");
> + if (this_subtest_failed)
> + return;
> + memset (buf, 0, TEST_BUFFER_SIZE);
> +}
> +
> +static void
> +setup_explicit_clear (void)
> +{
> + unsigned char buf[TEST_BUFFER_SIZE];
> + prepare_test_buffer (buf, "explicit clear");
> + if (this_subtest_failed)
> + return;
> + explicit_bzero (buf, TEST_BUFFER_SIZE);
> +}
> +
> +struct subtest
> +{
> + void (*setup_subtest) (void);
> + const char *label;
> + enum test_expectation expected;
> +};
> +
> +static const struct subtest subtests[] =
> +{
> + { setup_no_clear, "no clear", EXPECT_SOME },
> + { setup_ordinary_clear, "ordinary clear", EXPECT_SOME },
> + { setup_explicit_clear, "explicit clear", EXPECT_NONE },
> + { 0, 0, -1 }
> +};
> +static const struct subtest *this_subtest;
> +
> +/* This function is called as a signal handler. The signal is
> + triggered by a call to sigsuspend, therefore it is safe to do
> + non-async-signal-safe things within this function. */
> +static void
> +do_subtest (int signo __attribute__ ((unused)))
> +{
> + this_subtest->setup_subtest ();
> + if (!this_subtest_failed)
> + check_test_buffer (this_subtest->expected, this_subtest->label, "test");
> +}
> +
> +static int
> +do_test (void)
> +{
> + /* test-skeleton.c unconditionally sets stdout to be unbuffered.
> + vfprintf allocates a great deal of memory on the stack if called
> + with an unbuffered FILE*, overflowing the alt-stack. Reset stdout
> + to fully buffered to prevent this. */
> + if (setvbuf (stdout, 0, _IOFBF, 0))
> + {
> + xprintf ("ERROR: restoring stdout buffering: %s\n", strerror (errno));
> + return 2;
> + }
> +
> + size_t page_alignment = sysconf (_SC_PAGESIZE);
> + if (page_alignment < sizeof (void *))
> + page_alignment = sizeof (void *);
> +
> + void *p;
> + int err = posix_memalign (&p, page_alignment, SIGNAL_STACK_SIZE);
> + if (err || !p)
> + {
> + xprintf ("ERROR: allocating alt stack: %s\n", strerror (err));
> + return 2;
> + }
> + signal_stack_buffer = p;
> +
> + /* This program will malfunction if it receives SIGUSR1 signals at any
> + time other than when it's just sent one to itself. Therefore, keep
> + it blocked most of the time. */
> + sigset_t normal_mask;
> + sigset_t sigsusp_mask;
> + sigemptyset (&normal_mask);
> + sigaddset (&normal_mask, SIGUSR1);
> + if (sigprocmask (SIG_BLOCK, &normal_mask, &sigsusp_mask))
> + {
> + xprintf ("ERROR: block(SIGUSR1): %s\n", strerror (errno));
> + return 2;
> + }
> + /* But ensure that SIGUSR1 is _not_ blocked during calls to
> + sigsuspend, even if the invoking process blocked it. */
> + sigdelset (&sigsusp_mask, SIGUSR1);
> +
> + stack_t ss;
> + ss.ss_sp = signal_stack_buffer;
> + ss.ss_flags = 0;
> + ss.ss_size = SIGNAL_STACK_SIZE;
> + if (sigaltstack (&ss, 0))
> + {
> + xprintf ("ERROR: sigaltstack: %s\n", strerror (errno));
> + return 2;
> + }
> +
> + struct sigaction sa;
> + sa.sa_mask = normal_mask;
> + sa.sa_handler = do_subtest;
> + sa.sa_flags = SA_RESTART | SA_ONSTACK;
> + if (sigaction (SIGUSR1, &sa, 0))
> + {
> + xprintf ("ERROR: sigaction(SIGUSR1): %s\n", strerror (errno));
> + return 2;
> + }
> +
> + /* We use kill instead of raise, because raise may internally modify
> + the signal mask. */
> + pid_t self = getpid ();
> +
> + this_subtest = subtests;
> + while (this_subtest->label)
> + {
> + this_subtest_failed = false;
> +
> + /* Completely clear the signal stack between tests, so that junk
> + from previous tests cannot interfere with the current one. */
> + memset (signal_stack_buffer, 0, SIGNAL_STACK_SIZE);
> +
> + /* Raise SIGUSR1, then call sigsuspend with a mask that unblocks
> + SIGUSR1. This will trigger do_subtest to run _once_ on the
> + alternative stack, at the point of the sigsuspend. */
> + if (kill (self, SIGUSR1))
> + {
> + xprintf ("ERROR: raise(SIGUSR1): %s\n", strerror (errno));
> + return 2;
> + }
> + /* The return value of sigsuspend is not meaningful. */
> + sigsuspend (&sigsusp_mask);
> +
> + this_subtest++;
> + }
> +
> + return failed_subtests ? 1 : 0;
> +}
> +
> +#undef printf
> +#define TEST_FUNCTION do_test ()
> +#include "../test-skeleton.c"
> diff --git a/sysdeps/arm/nacl/libc.abilist b/sysdeps/arm/nacl/libc.abilist
> index 807e43d..b074374 100644
> --- a/sysdeps/arm/nacl/libc.abilist
> +++ b/sysdeps/arm/nacl/libc.abilist
> @@ -1843,6 +1843,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 gnu_dev_major F
> GLIBC_2.25 gnu_dev_makedev F
> GLIBC_2.25 gnu_dev_minor F
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index 77accdf..e265325 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2090,6 +2090,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 659b7fc..393dd4c 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2001,6 +2001,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index 8bc979a..b92eccb 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -91,6 +91,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 299b705..3b1661d 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1855,6 +1855,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index f00345f..452e013 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2013,6 +2013,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index e5fcf88..0196234 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1877,6 +1877,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 8f382f6..d6e0e16 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -92,6 +92,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index 320b7fe..c3a5f24 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1969,6 +1969,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index 21b1426..fa72a0d 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2090,6 +2090,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index 5c4b596..03d3c0c 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1944,6 +1944,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 001fa6c..a1c9cd2 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1942,6 +1942,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 2d87001..a8671ba 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1940,6 +1940,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index aa1ee66..e71283f 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1935,6 +1935,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 2471d68..f92545c 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2131,6 +2131,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 4b0cde8..d8427ca 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1973,6 +1973,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index 0557c16..fa26199 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1978,6 +1978,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> index 821384e..73488a4 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2178,6 +2178,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index c40a3f1..ac9cc69 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -92,6 +92,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 5b39a60..933a00a 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1973,6 +1973,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index a9db32f..eb74169 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1874,6 +1874,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index 294af0a..37aded2 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1859,6 +1859,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 32747bd..5b7c54e 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1965,6 +1965,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index b0ac4d4..4854351 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1903,6 +1903,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> index 4d92d81..18f61ba 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> @@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> index a68aef7..53a0db1 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> @@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> index 4d92d81..18f61ba 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> @@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index b8623fc..d4add67 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1854,6 +1854,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index a61d874..18717fa 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
> GLIBC_2.24 GLIBC_2.24 A
> GLIBC_2.24 quick_exit F
> GLIBC_2.25 GLIBC_2.25 A
> +GLIBC_2.25 __explicit_bzero F
> +GLIBC_2.25 __glibc_read_memory F
> +GLIBC_2.25 explicit_bzero F
> GLIBC_2.25 strfromd F
> GLIBC_2.25 strfromf F
> GLIBC_2.25 strfroml F
>
@@ -100,6 +100,18 @@ extern __typeof (memmem) __memmem;
libc_hidden_proto (__memmem)
libc_hidden_proto (__ffs)
+/* explicit_bzero is used in libcrypt. */
+extern __typeof (explicit_bzero) __explicit_bzero;
+extern __typeof (explicit_bzero) __internal_explicit_bzero;
+libc_hidden_proto (__internal_explicit_bzero)
+extern __typeof (__glibc_read_memory) __internal_glibc_read_memory;
+libc_hidden_proto (__internal_glibc_read_memory)
+/* Honor string[23].h overrides when present. */
+#ifdef explicit_bzero
+# define __explicit_bzero(s,n) explicit_bzero (s,n)
+# define __internal_explicit_bzero(s,n) explicit_bzero (s,n)
+#endif
+
libc_hidden_builtin_proto (memchr)
libc_hidden_builtin_proto (memcpy)
libc_hidden_builtin_proto (mempcpy)
@@ -34,6 +34,8 @@ too.
* Search Functions:: Searching for a specific element or substring.
* Finding Tokens in a String:: Splitting a string into tokens by looking
for delimiters.
+* Erasing Sensitive Data:: Clearing memory which contains sensitive
+ data, after it's no longer needed.
* strfry:: Function for flash-cooking a string.
* Trivial Encryption:: Obscuring data.
* Encode Binary Data:: Encoding and Decoding of Binary Data.
@@ -2404,6 +2406,128 @@ contains no '/' bytes, then "." is returned. The prototype for this
function can be found in @file{libgen.h}.
@end deftypefun
+@node Erasing Sensitive Data
+@section Erasing Sensitive Data
+
+It is sometimes necessary to make sure that data in memory is erased
+after use, even if no correct C program could access it again. For
+instance, a cryptographic key should not be left in memory after the
+program is finished using it, because there might be a bug that causes
+junk data, including the key, to be revealed to the outside world.
+
+However, the C compiler knows that no correct program can access data
+after it's deallocated, so it may delete ``unnecessary'' stores that
+erase data just before deallocating it. @code{memset}, @code{bzero},
+and a manually-written loop are all equally unreliable, and
+@code{volatile} will not help.
+
+For this situation, @theglibc{} provides @code{explicit_bzero}. It is
+functionally identical to @code{bzero}, but the C compiler will not
+treat any use of it as unnecessary.
+
+@comment string.h
+@comment BSD
+@deftypefun void explicit_bzero (void *@var{block}, size_t @var{len})
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+
+@code{explicit_bzero} writes zero into each of the first @var{len}
+bytes of the object beginning at @var{block}, just as @code{bzero}
+would. The zeroes are always written, even if the object is about to
+be deallocated. (Variables on the stack are deallocated when they go
+out of scope; heap blocks created by @code{malloc} are deallocated
+when passed to @code{free}.)
+
+@smallexample
+@group
+#include <string.h>
+
+extern void encrypt (const char *key, const char *in,
+ char *out, size_t n);
+extern void genkey (const char *phrase, char *key);
+
+void encrypt_with_phrase (const char *phrase, const char *in,
+ char *out, size_t n)
+@{
+ char key[16];
+ genkey (phrase, key);
+ encrypt (key, in, out, n);
+ explicit_bzero (key, 16);
+@}
+@end group
+@end smallexample
+
+@noindent
+If @code{bzero} or @code{memset} had been used in this function, the C
+compiler might remove it as unnecessary, but it will not do this with
+@code{explicit_bzero}.
+
+@strong{Warning:} The @emph{only} optimization disabled by
+@code{explicit_bzero} is removal of ``unnecessary'' writes to memory.
+In all other respects, the compiler is allowed to optimize as it would
+for @code{memset}. For instance, it may replace the function call
+with inline memory writes, and it may deduce that @var{block} cannot
+be a null pointer.
+
+@strong{Warning:} The compiler may make copies of any object, or parts
+of it, in temporary storage areas, such as registers and ``scratch''
+stack space. @code{explicit_bzero} does not erase copies of sensitive
+data. At present, there is no way to prevent temporary copies from
+being made, nor to arrange for them to be erased. Declaring sensitive
+variables as @code{volatile} will make the problem @emph{worse}; the
+compiler needs to make @emph{more} copies of @code{volatile} data in
+order to operate on it correctly.
+
+@strong{Warning:} In some situations, using @code{explicit_bzero} will
+@emph{cause} creation of an additional copy of sensitive data, and
+only that copy will be cleared:
+
+@smallexample
+@group
+#include <string.h>
+
+struct key
+@{
+ unsigned long long low;
+ unsigned long long high;
+@};
+
+struct key get_key (void);
+void use_key (struct key);
+
+void
+with_clear (void)
+@{
+ struct key k;
+ k = get_key ();
+ use_key (k);
+ explicit_bzero (&k, sizeof (k));
+@}
+@end group
+@end smallexample
+
+@noindent
+Without the call to @code{explicit_bzero}, @var{k} might not need to
+be stored in memory: perhaps its value could be returned from
+@code{get_key} and passed to @code{use_key} using only CPU registers.
+@code{explicit_bzero} operates on memory, so the compiler has to make
+a copy of @var{k} in memory for it, and the original in the CPU
+registers remains intact. This can occur for any variable whose
+address is only taken in a call to @code{explicit_bzero}, even if it
+might seem ``too large'' to be stored in registers.
+
+There is currently no way to avoid this problem. @Theglibc{}'s
+implementation of @code{explicit_bzero} contains a hack that can
+sometimes prevent the sensitive data from being copied into memory,
+but it is not guaranteed to work, and the original in the CPU
+registers is unaffected. Again, declaring sensitive variables as
+@code{volatile} will make the problem worse.
+
+@strong{Portability Note:} This function first appeared in OpenBSD 5.5
+and has not been standardized. @Theglibc{} declares this function in
+@file{string.h}, but on other systems it may be in @file{strings.h}
+instead.
+@end deftypefun
+
@node strfry
@section strfry
@@ -41,20 +41,26 @@ routines := strcat strchr strcmp strcoll strcpy strcspn \
addsep replace) \
envz basename \
strcoll_l strxfrm_l string-inlines memrchr \
- xpg-strerror strerror_l
+ xpg-strerror strerror_l explicit_bzero
+
+# Attention future hackers trying to enable link-time optimization for
+# glibc: this file *must not* be subject to LTO. It is added separately
+# to 'routines' to document this. See comments in this file for details.
+routines += read_memory
strop-tests := memchr memcmp memcpy memmove mempcpy memset memccpy \
stpcpy stpncpy strcat strchr strcmp strcpy strcspn \
strlen strncmp strncpy strpbrk strrchr strspn memmem \
strstr strcasestr strnlen strcasecmp strncasecmp \
- strncat rawmemchr strchrnul bcopy bzero memrchr
+ strncat rawmemchr strchrnul bcopy bzero memrchr \
+ explicit_bzero
tests := tester inl-tester noinl-tester testcopy test-ffs \
tst-strlen stratcliff tst-svc tst-inlcall \
bug-strncat1 bug-strspn1 bug-strpbrk1 tst-bswap \
tst-strtok tst-strxfrm bug-strcoll1 tst-strfry \
bug-strtok1 $(addprefix test-,$(strop-tests)) \
bug-envz1 tst-strxfrm2 tst-endian tst-svc2 \
- tst-strtok_r bug-strcoll2 tst-cmp
+ tst-strtok_r bug-strcoll2 tst-cmp tst-xbzero-opt
xtests = tst-strcoll-overflow
@@ -82,4 +82,14 @@ libc {
}
GLIBC_2.24 {
}
+ GLIBC_2.25 {
+ # used by inlines in bits/string2.h and bits/string3.h
+ __glibc_read_memory;
+
+ # used by libcrypt
+ __explicit_bzero;
+
+ # e*
+ explicit_bzero;
+ }
}
new file mode 100644
@@ -0,0 +1,33 @@
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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 the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <features.h>
+#undef __USE_STRING_INLINES
+#define __NO_STRING_INLINES
+#include <string.h>
+
+/* Set LEN bytes of S to 0. The compiler will not delete a call to
+ this function, even if S is dead after the call. */
+void
+__internal_explicit_bzero (void *s, size_t len)
+{
+ memset (s, '\0', len);
+ __internal_glibc_read_memory (s, len);
+}
+libc_hidden_def (__internal_explicit_bzero)
+strong_alias (__internal_explicit_bzero, __explicit_bzero)
+weak_alias (__internal_explicit_bzero, explicit_bzero)
new file mode 100644
@@ -0,0 +1,41 @@
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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 the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <string.h>
+
+/* This function is an optimization fence. It doesn't do anything
+ itself, but calls to it prevent calls to explicit_bzero from being
+ optimized away. In order to achieve this effect, this function
+ must never, under any circumstances, be inlined or subjected to
+ inter-procedural optimization. string.h declares this function
+ with attributes that, in conjunction with the no-op asm insert, are
+ sufficient to prevent problems in the current (2016) generation of
+ compilers, but *only if* this file is *not* compiled with -flto.
+ At present, this is not an issue since glibc is never compiled with
+ -flto, but should that ever change, this file must be excepted.
+
+ The 'volatile' below is technically not necessary but is included
+ for explicitness. */
+
+void
+internal_function
+__internal_glibc_read_memory(const void *s, size_t len)
+{
+ asm volatile ("");
+}
+libc_hidden_def (__internal_glibc_read_memory)
+strong_alias (__internal_glibc_read_memory, __glibc_read_memory)
@@ -455,6 +455,15 @@ extern void bcopy (const void *__src, void *__dest, size_t __n)
/* Set N bytes of S to 0. */
extern void bzero (void *__s, size_t __n) __THROW __nonnull ((1));
+/* As bzero, but the compiler will not delete a call to this
+ function, even if S is dead after the call. */
+extern void explicit_bzero (void *__s, size_t __n) __THROW __nonnull ((1));
+
+/* Optimization fence, used by bits/string2.h and bits/string3.h
+ inline versions of explicit_bzero. */
+extern void __glibc_read_memory (const void *__s, size_t __n)
+ __THROW __nonnull ((1)) __attribute_noinline__;
+
/* Compare N bytes of S1 and S2 (same as memcmp). */
extern int bcmp (const void *__s1, const void *__s2, size_t __n)
__THROW __attribute_pure__ __nonnull ((1, 2));
new file mode 100644
@@ -0,0 +1,20 @@
+/* Test and measure explicit_bzero.
+ Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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 the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+#define TEST_EXPLICIT_BZERO
+#define TEST_BZERO
+#include "test-memset.c"
@@ -19,7 +19,11 @@
#define TEST_MAIN
#ifdef TEST_BZERO
-# define TEST_NAME "bzero"
+# ifdef TEST_EXPLICIT_BZERO
+# define TEST_NAME "explicit_bzero"
+# else
+# define TEST_NAME "bzero"
+# endif
#else
# ifndef WIDE
# define TEST_NAME "memset"
@@ -56,7 +60,11 @@ void builtin_bzero (char *, size_t);
IMPL (simple_bzero, 0)
IMPL (builtin_bzero, 0)
+#ifdef TEST_EXPLICIT_BZERO
+IMPL (explicit_bzero, 1)
+#else
IMPL (bzero, 1)
+#endif
void
simple_bzero (char *s, size_t n)
new file mode 100644
@@ -0,0 +1,348 @@
+/* Test that explicit_bzero block clears are not optimized out.
+ Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library 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.
+
+ The GNU C Library 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 the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* This test is conceptually based on a test designed by Matthew
+ Dempsky for the OpenBSD regression suite:
+ <openbsd>/src/regress/lib/libc/explicit_bzero/explicit_bzero.c.
+ The basic idea is, we have a function that contains a
+ block-clearing operation (not necessarily explicit_bzero), after
+ which the block is dead, in the compiler-jargon sense. Execute
+ that function from a signal handler running on an alternative
+ signal stack. Then we have another pointer to the memory region
+ affected by the block clear -- namely, the sigaltstack buffer --
+ and can find out whether it actually happened.
+
+ The OpenBSD test cautions that some operating systems (e.g. Solaris
+ and OSX) wipe the signal stack upon returning to the normal stack,
+ so the test has to happen while still executing on the signal
+ stack. This, of course, means that the buffer might be clobbered
+ by normal stack operations after the function with the block clear
+ returns (it has to return, so that the block is truly dead). The
+ most straightforward way to deal with this is to have a large block
+ containing several copies of a byte pattern that is unlikely to
+ occur by chance, and check whether _any_ of them survives. */
+
+#define _GNU_SOURCE 1
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* test-skeleton.c unconditionally sets stdout to be unbuffered.
+ vfprintf allocates a great deal of memory on the stack if called
+ with an unbuffered FILE*, overflowing the alt-stack. do_test
+ therefore resets stdout to fully buffered, and we use this wrapper
+ exclusively for output. */
+static void
+xprintf (const char *msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+ vfprintf (stdout, msg, ap);
+ va_end (ap);
+ fflush (stdout);
+}
+#define printf dont_use_printf_in_this_file
+
+/* The "byte pattern that is unlikely to occur by chance": the first
+ 16 prime numbers (OEIS A000040). */
+static const unsigned char test_pattern[16] =
+{
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53
+};
+
+#define PATTERN_SIZE (sizeof test_pattern)
+#define PATTERN_REPS 32
+#define TEST_BUFFER_SIZE (PATTERN_SIZE * PATTERN_REPS)
+
+static void
+fill_with_test_pattern (unsigned char *buf)
+{
+ for (unsigned int i = 0; i < PATTERN_REPS; i++)
+ memcpy (buf + i*PATTERN_SIZE, test_pattern, PATTERN_SIZE);
+}
+
+static unsigned int
+count_test_patterns (unsigned char *buf, size_t bufsiz)
+{
+ unsigned char *first = memmem (buf, bufsiz, test_pattern, PATTERN_SIZE);
+ if (!first)
+ return 0;
+ unsigned int cnt = 0;
+ for (unsigned int i = 0; i < PATTERN_REPS; i++)
+ {
+ unsigned char *p = first + i*PATTERN_SIZE;
+ if (p + PATTERN_SIZE - buf > bufsiz)
+ break;
+ if (memcmp (p, test_pattern, PATTERN_SIZE) == 0)
+ cnt++;
+ }
+ return cnt;
+}
+
+/* Global test state. */
+static int failed_subtests;
+static bool this_subtest_failed;
+
+/* The signal stack is allocated with memalign. */
+static unsigned char *signal_stack_buffer;
+#define SIGNAL_STACK_SIZE (SIGSTKSZ + TEST_BUFFER_SIZE)
+
+enum test_expectation { EXPECT_NONE, EXPECT_SOME, EXPECT_ALL };
+
+static void
+check_test_buffer (enum test_expectation expected,
+ const char *label, const char *stage)
+{
+ unsigned int cnt = count_test_patterns (signal_stack_buffer,
+ SIGNAL_STACK_SIZE);
+ switch (expected)
+ {
+ case EXPECT_NONE:
+ if (cnt == 0)
+ xprintf ("PASS: %s/%s: expected 0 got %d\n", label, stage, cnt);
+ else
+ {
+ xprintf ("FAIL: %s/%s: expected 0 got %d\n", label, stage, cnt);
+ this_subtest_failed = true;
+ failed_subtests++;
+ }
+ break;
+
+ case EXPECT_SOME:
+ if (cnt > 0)
+ xprintf ("PASS: %s/%s: expected some got %d\n", label, stage, cnt);
+ else
+ {
+ xprintf ("FAIL: %s/%s: expected some got 0\n", label, stage);
+ this_subtest_failed = true;
+ failed_subtests++;
+ }
+ break;
+
+ case EXPECT_ALL:
+ if (cnt == PATTERN_REPS)
+ xprintf ("PASS: %s/%s: expected %d got %d\n", label, stage,
+ PATTERN_REPS, cnt);
+ else
+ {
+ xprintf ("FAIL: %s/%s: expected %d got %d\n", label, stage,
+ PATTERN_REPS, cnt);
+ this_subtest_failed = true;
+ failed_subtests++;
+ }
+ break;
+
+ default:
+ xprintf ("ERROR: %s/%s: invalid value for 'expected' = %d\n",
+ label, stage, (int)expected);
+ this_subtest_failed = true;
+ failed_subtests++;
+ }
+}
+
+/* Always check the test buffer immediately after filling it; this
+ makes externally visible side effects depend on the buffer existing
+ and having been filled in. */
+static void
+prepare_test_buffer (unsigned char *buf, const char *label)
+{
+ fill_with_test_pattern (buf);
+ check_test_buffer (EXPECT_ALL, label, "prepare");
+
+ unsigned char *loc = memmem (signal_stack_buffer, SIGNAL_STACK_SIZE,
+ test_pattern, PATTERN_SIZE);
+ if (loc == buf)
+ xprintf ("PASS: %s/prepare: expected buffer location %p got %p\n",
+ label, buf, loc);
+ else
+ {
+ xprintf ("FAIL: %s/prepare: expected buffer location %p got %p\n",
+ label, buf, loc);
+ this_subtest_failed = true;
+ failed_subtests++;
+ }
+}
+
+/* There are three subtests, two of which are sanity checks.
+
+ In the "no_clear" case, we don't do anything to the test buffer
+ between preparing it and letting it go out of scope, and we expect
+ to find it. This confirms that the test buffer does get filled in
+ and we can find it from the stack buffer. In the "ordinary_clear"
+ case, we clear it using memset, and we expect to find it. This
+ confirms that the compiler can optimize out block clears in this
+ context; if it can't, the real test might be succeeding for the
+ wrong reason. Finally, the "explicit_clear" case uses
+ explicit_bzero and expects _not_ to find the test buffer, which is
+ the real test. */
+
+static void
+setup_no_clear (void)
+{
+ unsigned char buf[TEST_BUFFER_SIZE];
+ prepare_test_buffer (buf, "no clear");
+}
+
+static void
+setup_ordinary_clear (void)
+{
+ unsigned char buf[TEST_BUFFER_SIZE];
+ prepare_test_buffer (buf, "ordinary clear");
+ if (this_subtest_failed)
+ return;
+ memset (buf, 0, TEST_BUFFER_SIZE);
+}
+
+static void
+setup_explicit_clear (void)
+{
+ unsigned char buf[TEST_BUFFER_SIZE];
+ prepare_test_buffer (buf, "explicit clear");
+ if (this_subtest_failed)
+ return;
+ explicit_bzero (buf, TEST_BUFFER_SIZE);
+}
+
+struct subtest
+{
+ void (*setup_subtest) (void);
+ const char *label;
+ enum test_expectation expected;
+};
+
+static const struct subtest subtests[] =
+{
+ { setup_no_clear, "no clear", EXPECT_SOME },
+ { setup_ordinary_clear, "ordinary clear", EXPECT_SOME },
+ { setup_explicit_clear, "explicit clear", EXPECT_NONE },
+ { 0, 0, -1 }
+};
+static const struct subtest *this_subtest;
+
+/* This function is called as a signal handler. The signal is
+ triggered by a call to sigsuspend, therefore it is safe to do
+ non-async-signal-safe things within this function. */
+static void
+do_subtest (int signo __attribute__ ((unused)))
+{
+ this_subtest->setup_subtest ();
+ if (!this_subtest_failed)
+ check_test_buffer (this_subtest->expected, this_subtest->label, "test");
+}
+
+static int
+do_test (void)
+{
+ /* test-skeleton.c unconditionally sets stdout to be unbuffered.
+ vfprintf allocates a great deal of memory on the stack if called
+ with an unbuffered FILE*, overflowing the alt-stack. Reset stdout
+ to fully buffered to prevent this. */
+ if (setvbuf (stdout, 0, _IOFBF, 0))
+ {
+ xprintf ("ERROR: restoring stdout buffering: %s\n", strerror (errno));
+ return 2;
+ }
+
+ size_t page_alignment = sysconf (_SC_PAGESIZE);
+ if (page_alignment < sizeof (void *))
+ page_alignment = sizeof (void *);
+
+ void *p;
+ int err = posix_memalign (&p, page_alignment, SIGNAL_STACK_SIZE);
+ if (err || !p)
+ {
+ xprintf ("ERROR: allocating alt stack: %s\n", strerror (err));
+ return 2;
+ }
+ signal_stack_buffer = p;
+
+ /* This program will malfunction if it receives SIGUSR1 signals at any
+ time other than when it's just sent one to itself. Therefore, keep
+ it blocked most of the time. */
+ sigset_t normal_mask;
+ sigset_t sigsusp_mask;
+ sigemptyset (&normal_mask);
+ sigaddset (&normal_mask, SIGUSR1);
+ if (sigprocmask (SIG_BLOCK, &normal_mask, &sigsusp_mask))
+ {
+ xprintf ("ERROR: block(SIGUSR1): %s\n", strerror (errno));
+ return 2;
+ }
+ /* But ensure that SIGUSR1 is _not_ blocked during calls to
+ sigsuspend, even if the invoking process blocked it. */
+ sigdelset (&sigsusp_mask, SIGUSR1);
+
+ stack_t ss;
+ ss.ss_sp = signal_stack_buffer;
+ ss.ss_flags = 0;
+ ss.ss_size = SIGNAL_STACK_SIZE;
+ if (sigaltstack (&ss, 0))
+ {
+ xprintf ("ERROR: sigaltstack: %s\n", strerror (errno));
+ return 2;
+ }
+
+ struct sigaction sa;
+ sa.sa_mask = normal_mask;
+ sa.sa_handler = do_subtest;
+ sa.sa_flags = SA_RESTART | SA_ONSTACK;
+ if (sigaction (SIGUSR1, &sa, 0))
+ {
+ xprintf ("ERROR: sigaction(SIGUSR1): %s\n", strerror (errno));
+ return 2;
+ }
+
+ /* We use kill instead of raise, because raise may internally modify
+ the signal mask. */
+ pid_t self = getpid ();
+
+ this_subtest = subtests;
+ while (this_subtest->label)
+ {
+ this_subtest_failed = false;
+
+ /* Completely clear the signal stack between tests, so that junk
+ from previous tests cannot interfere with the current one. */
+ memset (signal_stack_buffer, 0, SIGNAL_STACK_SIZE);
+
+ /* Raise SIGUSR1, then call sigsuspend with a mask that unblocks
+ SIGUSR1. This will trigger do_subtest to run _once_ on the
+ alternative stack, at the point of the sigsuspend. */
+ if (kill (self, SIGUSR1))
+ {
+ xprintf ("ERROR: raise(SIGUSR1): %s\n", strerror (errno));
+ return 2;
+ }
+ /* The return value of sigsuspend is not meaningful. */
+ sigsuspend (&sigsusp_mask);
+
+ this_subtest++;
+ }
+
+ return failed_subtests ? 1 : 0;
+}
+
+#undef printf
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
@@ -1843,6 +1843,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 gnu_dev_major F
GLIBC_2.25 gnu_dev_makedev F
GLIBC_2.25 gnu_dev_minor F
@@ -2090,6 +2090,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2001,6 +2001,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -91,6 +91,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1855,6 +1855,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2013,6 +2013,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1877,6 +1877,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -92,6 +92,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1969,6 +1969,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2090,6 +2090,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1944,6 +1944,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1942,6 +1942,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1940,6 +1940,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1935,6 +1935,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2131,6 +2131,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1973,6 +1973,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1978,6 +1978,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2178,6 +2178,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -92,6 +92,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1973,6 +1973,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1874,6 +1874,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1859,6 +1859,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1965,6 +1965,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1903,6 +1903,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -1854,6 +1854,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F
@@ -2097,6 +2097,9 @@ GLIBC_2.23 fts64_set F
GLIBC_2.24 GLIBC_2.24 A
GLIBC_2.24 quick_exit F
GLIBC_2.25 GLIBC_2.25 A
+GLIBC_2.25 __explicit_bzero F
+GLIBC_2.25 __glibc_read_memory F
+GLIBC_2.25 explicit_bzero F
GLIBC_2.25 strfromd F
GLIBC_2.25 strfromf F
GLIBC_2.25 strfroml F