[RFC] [PATCH] Support explicit_bzero, memset_s, memzero_explicit, or similar.

Nick Mathewson nickm@freehaven.net
Mon Dec 15 14:21:00 GMT 2014


Hello!

(This is my first attempt at a glibc RFC/patch post.  I'm hoping I can
do more in the future, if this works out.)


It's frequently desirable in secure C programming to cause a piece
of memory to be erased in a way that the compiler is not allowed to
optimize away.

For example, if you're writing a program that needs to perform a
single operation with a high-value password cryptographic key, you
might want to make sure that the key is no longer resident in memory
once you're done with it.  But this pattern is inadequate to do so:

    int password_is_okay(void)
    {
       int result = -1;
       char passwordbuf[MAX_PASSWORD + 1];
       if (read_password(passwordbuf, MAX_PASSWORD) < 0)
         goto err;
       if (password_matches(passwordbuf) < 0)
         goto err;

       result = 0;
     err:
       memset(passwordbuf, 0, sizeof(passwordbuf));
       return result;
    }

The pattern above fails because the memset call is a dead store, and
the compiler is allowed to eliminate it.


Different operating systems, libraries, and standards have
approached this problem differently.  Windows provides
      SecureZeroMemory(void *, size_t);
The BSD family is moving towards
      explicit_bzero(void *, size_t);
And the C11 standard defines
      memset_s(void *, rsize_t, int, size_t);
And OpenSSL (along with LibreSSL and BoringSSL) provide:
      OPENSSL_cleanse(void *, size_t);
And in the Linux kernel these says, they're using:
      memzero_explicit(void *, size_t);


These APIs are in wide use where available:
     http://codesearch.debian.net/results/memset_s/page_0
     http://codesearch.debian.net/results/explicit_bzero/page_0
especially in cryptographic and security-related tools.  In the
absence of these APIs, programs typically try to cobble their own
implementations together -- typically using some combination of
volatile and explicit barriers.


I propose that glibc add such an API: either memset_s (which has a
standard backing it), or explicit_bzero (which has been around
longer, and is pretty widely used), or memzero_explicit (if we feel
strongly that even mentioning bzero is deprecated.)


Now, at first glance it would seem that that memset_s() is the
obvious choice, since it's the one supported by a standard.  But
it's a part of a much larger pile of bounds-checking variants of
other C functions (C11 Annex K).  It's *possible* to
implement memset_s() without the rest of Annex K, but it requires a
bit of infrastructure to be fully compliant.  (Like, we would need
an rsize_t, and a set_constraint_handler_s().  I do not believe we
would need a full implementation of Annex K.)

It's possible to do a non-compliant memset_s() implementation, and
not unprecedented.  The OSX version, for instance, doesn't include
set_constraint_handler_s(), or any other Annex K functions as far as
I can tell.

On the other hand, if we think that memset_s() is the only useful
part of Annex K, maybe memset_s() isn't the variant for us?



I'm including an example manpage for a somewhat-compliant
memset_s(), and one for an explicit_bzero.

I'm also attaching an incomplete explicit_bzero implementation patch
-- this is my first glibc patch, so I have probably made many
mistakes.  I can write an memset_s one instead if needed.


I hereby dedicate this email, the patch attached to it, and all
later versions of this patch written by me to the public domain.


========== variant 1
NAME
       memset_s - fill memory with a constant byte, securely.

SYNOPSIS

       #define __STDC_WANT_LIB_EXT1__ 1
       #include <string.h>

       errno_t memset_s(void *s, rsize_t szmax, int c, rsize_t n);

DESCRIPTION
       The memset() function fills the first n bytes of the memory
       area pointed to by s with the constant byte c.

       The szmax value indicates the length of the memory area at s;
       it must be no smaller than the value of n.  If it is, a
       runtime constraint violation occurs.

       Unlike memset(), the memset_s() function will not be removed
       by the compiler, even if it can prove that there are no
       subsequent legal reads to the memory in s.

RETURN VALUE
       The memset_s() function zero if no constraint violation
       occurred.  Otherwise, it returns an error.

CONFORMING TO
       C11 Annex K specifies a memset_s().  This implementation
       does not provide set_constraint_handler_s().

SEE ALSO
       bzero(3), memset(3)
==========




========== variant 2
NAME
       explicit_bzero - fill memory with zero, securely.

SYNOPSIS
       #include <string.h>

       void explicit_bzero(void *s, size_t n);

DESCRIPTION
       The explicit_bzero() function sets the first n bytes of the
       memory area starting at 's' to zero (bytes containing '\0').

       Unlike memset() and the regular bzero() function, the
       explicit_bzero() function will not be removed by the compiler,
       even if it can prove that there are no subsequent legal reads
       to the memory in s.

RETURN VALUE
       None.

CONFORMING TO
       explicit_bzero() first appeared in OpenBSD 5.5.

SEE ALSO
       bzero(3), memset(3)
==========
-------------- next part --------------
A non-text attachment was scrubbed...
Name: explicit_bzero_v1.patch
Type: text/x-patch
Size: 8995 bytes
Desc: not available
URL: <http://sourceware.org/pipermail/libc-alpha/attachments/20141215/eaaad718/attachment.bin>


More information about the Libc-alpha mailing list