Hi,

this introduces a new allocation function thet blends calloc(3) and
reallocarray(4) with the additional feature that released memory (to
the libc malloc cache) is always cleared. This is primarily
usesful for resizing objects holding sensitive data like keys.

The presented portable implementation does not use malloc internals,
and as such is not the fastest possible. Later I will make a version
that uses malloc internal meta-data to make it posible to catch wrong
usage and/or make it quicker.

A minor libc bump would be needed.

OK? 

        -Otto

Index: Symbols.list
===================================================================
RCS file: /cvs/src/lib/libc/Symbols.list,v
retrieving revision 1.50
diff -u -p -r1.50 Symbols.list
--- Symbols.list        3 Sep 2016 16:25:03 -0000       1.50
+++ Symbols.list        2 Mar 2017 14:34:17 -0000
@@ -1470,6 +1470,7 @@ qsort
 radixsort
 realloc
 reallocarray
+recallocarray
 realpath
 remque
 setenv
Index: hidden/stdlib.h
===================================================================
RCS file: /cvs/src/lib/libc/hidden/stdlib.h,v
retrieving revision 1.8
diff -u -p -r1.8 stdlib.h
--- hidden/stdlib.h     14 Aug 2016 23:18:03 -0000      1.8
+++ hidden/stdlib.h     2 Mar 2017 14:34:17 -0000
@@ -128,6 +128,7 @@ PROTO_NORMAL(rand_r);
 PROTO_DEPRECATED(random);
 /*PROTO_NORMAL(realloc);               not yet, breaks emacs */
 PROTO_NORMAL(reallocarray);
+PROTO_NORMAL(recallocarray);
 PROTO_DEPRECATED(realpath);
 PROTO_DEPRECATED(seed48);
 PROTO_NORMAL(seed48_deterministic);
Index: stdlib/Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/stdlib/Makefile.inc,v
retrieving revision 1.61
diff -u -p -r1.61 Makefile.inc
--- stdlib/Makefile.inc 14 Aug 2016 23:18:03 -0000      1.61
+++ stdlib/Makefile.inc 2 Mar 2017 14:34:17 -0000
@@ -7,6 +7,7 @@ SRCS+=  a64l.c abort.c atexit.c atoi.c at
        exit.c ecvt.c gcvt.c getenv.c getopt_long.c \
        getsubopt.c hcreate.c heapsort.c imaxabs.c imaxdiv.c insque.c \
        l64a.c llabs.c lldiv.c lsearch.c malloc.c reallocarray.c \
+       recallocarray.c \
        merge.c posix_pty.c qsort.c radixsort.c rand.c random.c \
        realpath.c remque.c setenv.c strtoimax.c \
        strtol.c strtoll.c strtonum.c strtoul.c strtoull.c strtoumax.c \
Index: stdlib/malloc.3
===================================================================
RCS file: /cvs/src/lib/libc/stdlib/malloc.3,v
retrieving revision 1.101
diff -u -p -r1.101 malloc.3
--- stdlib/malloc.3     12 Feb 2017 10:46:09 -0000      1.101
+++ stdlib/malloc.3     2 Mar 2017 14:34:17 -0000
@@ -51,6 +51,8 @@
 .Ft void *
 .Fn reallocarray "void *ptr" "size_t nmemb" "size_t size"
 .Ft void *
+.Fn recallocarray "void *ptr" "size_t oldnmemb" "size_t nmemb" "size_t size"
+.Ft void *
 .Fn realloc "void *ptr" "size_t size"
 .Ft void
 .Fn free "void *ptr"
@@ -113,6 +115,33 @@ and checks for integer overflow in the c
 .Fa size .
 .Pp
 The
+.Fn recallocarray
+function is similar to
+.Fn reallocarray
+except that it takes care of clearing newly allocated and freed memory.
+If
+.Fa ptr
+is a
+.Dv NULL
+pointer,
+.Fa oldnmemb
+is ignored and the call is equivalent to
+.Fn calloc .
+If
+.Fa ptr
+is not a
+.Dv NULL
+pointer,
+.Fa oldnmemb
+must be a value such that
+.Fa oldnmemb
+* 
+.Fa size
+is the size of an earlier allocation that returned
+.Fa ptr ,
+otherwise the behaviour is undefined.
+.Pp
+The
 .Fn free
 function causes the space pointed to by
 .Fa ptr
@@ -129,16 +158,18 @@ If
 was previously freed by
 .Fn free ,
 .Fn realloc ,
+.Fn reallocarray
 or
-.Fn reallocarray ,
+.Fn recallocarray ,
 the behavior is undefined and the double free is a security concern.
 .Sh RETURN VALUES
 Upon successful completion, the functions
 .Fn malloc ,
 .Fn calloc ,
 .Fn realloc ,
-and
 .Fn reallocarray
+and
+.Fn recallocarray
 return a pointer to the allocated space; otherwise, a
 .Dv NULL
 pointer is returned and
@@ -161,15 +192,31 @@ If multiplying
 and
 .Fa size
 results in integer overflow,
-.Fn calloc
-and
+.Fn calloc ,
 .Fn reallocarray
+and
+.Fn recallocarray
 return
 .Dv NULL
 and set
 .Va errno
 to
 .Er ENOMEM .
+.Pp
+If
+.Fa ptr 
+is not NULL and multiplying
+.Fa oldnmemb
+and
+.Fa size
+results in integer overflow
+.Fn recallocarray
+returns
+.Dv NULL
+and sets
+.Va errno
+to
+.Er EINVAL .
 .Sh IDIOMS
 Consider
 .Fn calloc
@@ -264,6 +311,17 @@ Use the following:
 .Bd -literal -offset indent
 newp = realloc(p, newsize);
 .Ed
+.Pp
+The
+.Fn recallocarray
+function should be used for resizing objects containing sensitive data like
+keys.
+To avoid leaking information,
+it guarantees memory is cleared before placing it on the internal free list.
+A
+.Fn free
+call for such an object should still be preceded by a call to
+.Xr explicit_bzero 3 .
 .Sh ENVIRONMENT
 .Bl -tag -width "/etc/malloc.conf"
 .It Ev MALLOC_OPTIONS
Index: stdlib/recallocarray.c
===================================================================
RCS file: stdlib/recallocarray.c
diff -N stdlib/recallocarray.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ stdlib/recallocarray.c      2 Mar 2017 14:34:17 -0000
@@ -0,0 +1,81 @@
+/*     $OpenBSD$       */
+/*
+ * Copyright (c) 2008, 2017 Otto Moerbeek <o...@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
+{
+       size_t oldsize, newsize;
+       void *newptr;
+
+       if (ptr == NULL)
+               return calloc(newnmemb, size);
+
+       if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+           newnmemb > 0 && SIZE_MAX / newnmemb < size) {
+               errno = ENOMEM;
+               return NULL;
+        }
+       newsize = newnmemb * size;
+
+       if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+           oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
+               errno = EINVAL;
+               return NULL;
+       }
+       oldsize = oldnmemb * size;
+       
+       /*
+        * Don't bother too much if we're shrinking just a bit,
+        * we do not shrink for series of small steps, oh well.
+        */
+       if (newsize <= oldsize) {
+               size_t d = oldsize - newsize;
+
+               if (d < oldsize / 2 && d < getpagesize()) {
+                       memset((char *)ptr + newsize, 0, d);
+                       return ptr;
+               }
+       }
+
+       newptr = malloc(newsize);
+       if (newptr == NULL)
+               return NULL;
+
+       if (newsize > oldsize) {
+               memcpy(newptr, ptr, oldsize);
+               memset((char *)newptr + oldsize, 0, newsize - oldsize);
+       } else
+               memcpy(newptr, ptr, newsize);
+
+       explicit_bzero(ptr, oldsize);
+       free(ptr);
+
+       return newptr;
+}
+DEF_WEAK(recallocarray);

Reply via email to