This is the mail archive of the
mailing list for the glibc project.
Re: [RFC] [PATCH] Support explicit_bzero, memset_s, memzero_explicit, or similar.
- From: Rich Felker <dalias at libc dot org>
- To: libc-alpha at sourceware dot org
- Date: Mon, 15 Dec 2014 12:35:27 -0500
- Subject: Re: [RFC] [PATCH] Support explicit_bzero, memset_s, memzero_explicit, or similar.
- Authentication-results: sourceware.org; auth=none
- References: <CAKDKvuzWYf3GcXYs4ED8XLyy58nzmvxRV84xwsKKZjPpVSFQug at mail dot gmail dot com>
On Mon, Dec 15, 2014 at 09:21:07AM -0500, Nick Mathewson wrote:
> (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;
> 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);
None of these solve the problem, because the compiler is free to have
copied part of all of this buffer into other temporary storage on the
stack or registers. This is especially the case if SIMD optimizations
are used. Solving the problem (which isn't really even a problem, just
a hardening consideration) correctly requires compiler features.
> 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.)
errno_t is defined (by Annex K) as int and rsize_t is defined as
size_t, so we could just omit these types entirely and use the
standard types (int and size_t) and still not conflict with the Annex
K definition. There's no reason to use or even provide the ugly and
useless MS/Annex K typedefs.
> 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?
There are some other functions from Annex K that are possibly
desirable to adopt like qsort_s. IMO an ideal response to Annex K
would be to adopt the useful/usable subset of it, without stupid
things like constraint handlers, and aim to get those standardized in
POSIX while the rest is left to rot (and maybe even get deprecated in
a future version of ISO C).