Adding memset_s function

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Adding memset_s function

Alan Barrett
For some time now, I have wanted a function to zero a block of
memory, with a guarantee that the compiler will not optimise it
away and do nothing.

ISO/IEC 9899:2011 ("C 11") defines a memset_s function for that.
It's a lot like memset(3), but it takes an extra argument, does
some sanity checks on the arguments, and it's guaranteed not to be
optimised away by the compiler.

You can get a free copy of one of the last committee drafts
of the C11 standard from
<http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>,
but you have to pay for the actual standard.

A patch to add memset_s to NetBSD is here:
<ftp://ftp.netbsd.org/pub/NetBSD/misc/apb/memset_s.20120224.diff>
or via HTTP:
<http://ftp.netbsd.org/pub/NetBSD/misc/apb/memset_s.20120224.diff>.

In addition to the memset_s function, man page, and tests,
it also adds a few things to header files, protected by
"#if (__STDC_WANT_LIB_EXT1__ - 0 == 1) || defined(_NETBSD_SOURCE)"

     * errno.h: define errno_t;
     * stddef.h: define rsize_t;
     * string.h: define errno_t and rsize_t;
     * stdint.h: define RSIZE_MAX;

I have not implemented the set_constraint_handler_s interface
from ISO/IEC 9899:2011 section K.3.1.4; my current implementation
of memset_s behaves as if the runtime-constraint handler does
nothing.  I plan to add the constraint handler interface later.

The standard does not specify what errno values should be used, so
I just picked some.

The formatted man page for memset_s looks like this:

NAME
      memset_s -- copy a value to all bytes of a memory buffer

LIBRARY
      Standard C Library (libc, -lc)

SYNOPSIS
      #define __STDC_WANT_LIB_EXT1__ 1

      #include <string.h>

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

DESCRIPTION
      The memset_s() function copies the value c (converted to an
      unsigned char) into each of the first n bytes of the memory buffer
      whose starting address is given by s.

      It is a runtime-constraints violation if s is a null pointer, or
      if either of smax or n is larger than RSIZE_MAX, or if smax is
      smaller than n.  If there is a runtime-constraints violation,
      and if s is not a null pointer, and if smax is not larger than
      RSIZE_MAX, then, before reporting the runtime-constraints
      violation, memset_s() copies smax bytes to the destination.

      In contrast to the memset(3) function, calls to memset_s() will
      never be ``optimised away'' by a compiler.  This property is
      required by the following sentences in section K.3.7.4.1 of
      ISO/IEC 9899:2011 (``ISO C11''):

            Unlike memset(), any call to the memset_s() function shall
            be evaluated strictly according to the rules of the abstract
            machine as described in (5.1.2.3).  That is, any call to the
            memset_s() function shall assume that the memory indicated
            by s and n may be accessible in the future and thus must
            contain the values indicated by c.

RETURN VALUES
      The memset_s() function returns zero for success, or a non-zero
      error code if there was a runtime-constraints violation.

ERRORS
      memset_s() returns the following error codes, and also stores the error
      codes in the global errno variable:

      [EINVAL]           The s argument was a null pointer.

      [E2BIG]            One or both of smax or n was larger than RSIZE_MAX.

      [EOVERFLOW]        n was larger than smax.

SEE ALSO
      memset(3).

STANDARDS
      The memset_s() function conforms to ISO/IEC 9899:2011 (``ISO C11''),
      except that the set_constraint_handler_s() interface is not supported.



--apb (Alan Barrett)
Reply | Threaded
Open this post in threaded view
|

Re: Adding memset_s function

Joerg Sonnenberger
On Fri, Feb 24, 2012 at 12:06:01PM +0200, Alan Barrett wrote:
> ISO/IEC 9899:2011 ("C 11") defines a memset_s function for that.
> It's a lot like memset(3), but it takes an extra argument, does some
> sanity checks on the arguments, and it's guaranteed not to be
> optimised away by the compiler.

Frankly, I think we should first discuss whether we want to implement
the *_s crap first or not. IMO it's all a waste of disk space and time
and I have no idea what the Working Group was thinking when they even
considered it. I mean who else can see something wrong with a proposal
that includes gets_s and snprintf_s?

Joerg
Reply | Threaded
Open this post in threaded view
|

Re: Adding memset_s function

Martin Husemann
On Fri, Feb 24, 2012 at 01:38:21PM +0100, Joerg Sonnenberger wrote:
> Frankly, I think we should first discuss whether we want to implement
> the *_s crap first or not. IMO it's all a waste of disk space and time
> and I have no idea what the Working Group was thinking when they even
> considered it. I mean who else can see something wrong with a proposal
> that includes gets_s and snprintf_s?

I agree in general, but the memset_s (and IIRC sprintf_s) are mostly sane,
so I see no problem in cherry picking them.

Martin
Reply | Threaded
Open this post in threaded view
|

Re: Adding memset_s function

Alan Barrett
In reply to this post by Joerg Sonnenberger
On Fri, 24 Feb 2012, Joerg Sonnenberger wrote:
>On Fri, Feb 24, 2012 at 12:06:01PM +0200, Alan Barrett wrote:
>> ISO/IEC 9899:2011 ("C 11") defines a memset_s function for that.
>> It's a lot like memset(3), but it takes an extra argument, does some
>> sanity checks on the arguments, and it's guaranteed not to be
>> optimised away by the compiler.
>
>Frankly, I think we should first discuss whether we want to implement
>the *_s crap first or not.

Some of the *_s functions seem sensible, and some don't.  I do not
propose to add them all.

--apb (Alan Barrett)
Reply | Threaded
Open this post in threaded view
|

Re: Adding memset_s function

Alan Barrett
In reply to this post by Alan Barrett
On Fri, 24 Feb 2012, Alan Barrett wrote:
>For some time now, I have wanted a function to zero a block of memory,
>with a guarantee that the compiler will not optimise it away and do
>nothing.

Regardless of whether or not we add memset_s, perhaps we should
add something like this to libc:

        /*
         * memset_volatile is a volatile pointer to the memset function.
         * You can call (*memset_volatile)(buf, val, len) or even
         * memset_volatile(buf, val, len) just as you would call
         * memset(buf, val, len), but the use of a volatile pointer
         * guarantees that the compiler will not optimise the call away.
         */
        void * (* volatile memset_volatile)(void *, int, size_t) = memset;

There are several places where we use memset(buf, 0, len) to clear
a buffer that contains sensitive information (e.g. a password, or
crypto key), and changing them to use memset_volatile(buf, 0, len)
instead would ensure that the compiler does not optimise the calls
away.

--apb (Alan Barrett)