summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOtto Moerbeek <otto@cvs.openbsd.org>2017-03-06 18:44:22 +0000
committerOtto Moerbeek <otto@cvs.openbsd.org>2017-03-06 18:44:22 +0000
commit965060495a3fc62e23a3e71dfdcb3b967e90a5bc (patch)
treee5f7a40cd424d04a00e2af858db6aeb5ca74fce3
parentd702f193422415766095908d4c396d5228f3491c (diff)
Introducing recallocarray(3), a blend of calloc(3) and reallocarray(3)
with the added feature that released memory is cleared. Much input from various developers. ok deraadt@ tom@
-rw-r--r--lib/libc/Symbols.list1
-rw-r--r--lib/libc/hidden/stdlib.h3
-rw-r--r--lib/libc/stdlib/Makefile.inc3
-rw-r--r--lib/libc/stdlib/malloc.370
-rw-r--r--lib/libc/stdlib/recallocarray.c81
5 files changed, 150 insertions, 8 deletions
diff --git a/lib/libc/Symbols.list b/lib/libc/Symbols.list
index fa352f3cfe3..d4e0fa5898d 100644
--- a/lib/libc/Symbols.list
+++ b/lib/libc/Symbols.list
@@ -1470,6 +1470,7 @@ qsort
radixsort
realloc
reallocarray
+recallocarray
realpath
remque
setenv
diff --git a/lib/libc/hidden/stdlib.h b/lib/libc/hidden/stdlib.h
index 76a9c4d809c..63b7b5d9c70 100644
--- a/lib/libc/hidden/stdlib.h
+++ b/lib/libc/hidden/stdlib.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: stdlib.h,v 1.8 2016/08/14 23:18:03 guenther Exp $ */
+/* $OpenBSD: stdlib.h,v 1.9 2017/03/06 18:44:21 otto Exp $ */
/* $NetBSD: stdlib.h,v 1.25 1995/12/27 21:19:08 jtc Exp $ */
/*-
@@ -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);
diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc
index 3489e440211..36b5869adb2 100644
--- a/lib/libc/stdlib/Makefile.inc
+++ b/lib/libc/stdlib/Makefile.inc
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile.inc,v 1.61 2016/08/14 23:18:03 guenther Exp $
+# $OpenBSD: Makefile.inc,v 1.62 2017/03/06 18:44:21 otto Exp $
# stdlib sources
.PATH: ${LIBCSRCDIR}/arch/${MACHINE_CPU}/stdlib ${LIBCSRCDIR}/stdlib
@@ -7,6 +7,7 @@ SRCS+= a64l.c abort.c atexit.c atoi.c atof.c atol.c atoll.c bsearch.c \
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 \
diff --git a/lib/libc/stdlib/malloc.3 b/lib/libc/stdlib/malloc.3
index 1f80c3529ee..66de428cb0d 100644
--- a/lib/libc/stdlib/malloc.3
+++ b/lib/libc/stdlib/malloc.3
@@ -30,9 +30,9 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" $OpenBSD: malloc.3,v 1.101 2017/02/12 10:46:09 otto Exp $
+.\" $OpenBSD: malloc.3,v 1.102 2017/03/06 18:44:21 otto Exp $
.\"
-.Dd $Mdocdate: February 12 2017 $
+.Dd $Mdocdate: March 6 2017 $
.Dt MALLOC 3
.Os
.Sh NAME
@@ -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 calculation
.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
diff --git a/lib/libc/stdlib/recallocarray.c b/lib/libc/stdlib/recallocarray.c
new file mode 100644
index 00000000000..a2f37fe81a9
--- /dev/null
+++ b/lib/libc/stdlib/recallocarray.c
@@ -0,0 +1,81 @@
+/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */
+/*
+ * Copyright (c) 2008, 2017 Otto Moerbeek <otto@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);