From 3c33abc520d90bd1a0712a6406cd750b560de053 Mon Sep 17 00:00:00 2001 From: FRIGN Date: Tue, 10 Mar 2015 22:19:19 +0100 Subject: [PATCH] Implement mallocarray() A function used only in the OpenBSD-Kernel as of now, but it surely provides a helpful interface when you just don't want to make sure the incoming pointer to erealloc() is really NULL so it behaves like malloc, making it a bit more safer. Talking about *allocarray(): It's definitely a major step in code- hardening. Especially as a system administrator, you should be able to trust your core tools without having to worry about segfaults like this, which can easily lead to privilege escalation. How do the GNU coreutils handle this? $ strings -n 4611686018427387903 strings: invalid minimum string length -1 $ strings -n 4611686018427387904 strings: invalid minimum string length 0 They silently overflow... In comparison, sbase: $ strings -n 4611686018427387903 mallocarray: out of memory $ strings -n 4611686018427387904 mallocarray: out of memory The first out of memory is actually a true OOM returned by malloc, whereas the second one is a detected overflow, which is not marked in a special way. Now tell me which diagnostic error-messages are easier to understand. --- Makefile | 1 + col.c | 2 +- cut.c | 2 +- find.c | 8 +++---- libutil/mallocarray.c | 50 ++++++++++++++++++++++++++++++++++++++++++ libutil/reallocarray.c | 1 - ls.c | 4 ++-- paste.c | 8 +++---- printf.c | 4 ++-- sed.c | 5 ++--- strings.c | 4 ++-- tr.c | 6 ++--- util.h | 2 ++ 13 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 libutil/mallocarray.c diff --git a/Makefile b/Makefile index 5c55f43..7ca299b 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ LIBUTILSRC =\ libutil/fnck.c\ libutil/getlines.c\ libutil/human.c\ + libutil/mallocarray.c\ libutil/md5.c\ libutil/mode.c\ libutil/putword.c\ diff --git a/col.c b/col.c index 585d6aa..e6aa1c3 100644 --- a/col.c +++ b/col.c @@ -177,7 +177,7 @@ allocbuf(void) { char **bp; - buff = ereallocarray(buff, pagsize, sizeof(*buff)); + buff = emallocarray(pagsize, sizeof(*buff)); for (bp = buff; bp < &buff[pagsize]; ++bp) *bp = emalloc(NCOLS); } diff --git a/cut.c b/cut.c index 1658a5c..586636c 100644 --- a/cut.c +++ b/cut.c @@ -56,7 +56,7 @@ parselist(char *str) if (*s == ',') n++; } - r = ereallocarray(r, n, sizeof(*r)); + r = emallocarray(n, sizeof(*r)); for (s = str; n; n--, s++) { r->min = (*s == '-') ? 1 : strtoul(s, &s, 10); r->max = (*s == '-') ? strtoul(s + 1, &s, 10) : r->min; diff --git a/find.c b/find.c index ebea89b..91827c6 100644 --- a/find.c +++ b/find.c @@ -595,7 +595,7 @@ get_exec_arg(char *argv[], Extra *extra) e->u.p.arglen = e->u.p.filelen = 0; e->u.p.first = e->u.p.next = arg - argv - 1; e->u.p.cap = (arg - argv) * 2; - e->argv = ereallocarray(e->argv, e->u.p.cap, sizeof(*e->argv)); + e->argv = emallocarray(e->u.p.cap, sizeof(*e->argv)); for (arg = argv, new = e->argv; *arg; arg++, new++) { *new = *arg; @@ -604,7 +604,7 @@ get_exec_arg(char *argv[], Extra *extra) arg++; /* due to our extra NULL */ } else { e->argv = argv; - e->u.s.braces = ereallocarray(e->u.s.braces, ++nbraces, sizeof(*e->u.s.braces)); /* ++ for NULL */ + e->u.s.braces = emallocarray(++nbraces, sizeof(*e->u.s.braces)); /* ++ for NULL */ for (arg = argv, braces = e->u.s.braces; *arg; arg++) if (!strcmp(*arg, "{}")) @@ -632,7 +632,7 @@ get_ok_arg(char *argv[], Extra *extra) *arg = NULL; o->argv = argv; - o->braces = ereallocarray(o->braces, ++nbraces, sizeof(*o->braces)); + o->braces = emallocarray(++nbraces, sizeof(*o->braces)); for (arg = argv, braces = o->braces; *arg; arg++) if (!strcmp(*arg, "{}")) @@ -824,7 +824,7 @@ parse(int argc, char **argv) * https://en.wikipedia.org/wiki/Shunting-yard_algorithm * read from infix, resulting rpn ends up in rpn, next position in rpn is out * push operators onto stack, next position in stack is top */ - rpn = ereallocarray(rpn, ntok + gflags.print, sizeof(*rpn)); + rpn = emallocarray(ntok + gflags.print, sizeof(*rpn)); for (tok = infix, out = rpn, top = stack; tok->type != END; tok++) { switch (tok->type) { case PRIM: *out++ = *tok; break; diff --git a/libutil/mallocarray.c b/libutil/mallocarray.c new file mode 100644 index 0000000..e89dfd2 --- /dev/null +++ b/libutil/mallocarray.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * 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 +#include +#include +#include + +#include "../util.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 (1UL << (sizeof(size_t) * 4)) + +void * +mallocarray(size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return malloc(nmemb * size); +} + +void * +emallocarray(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = mallocarray(nmemb, size))) + eprintf("mallocarray: out of memory\n"); + + return p; +} diff --git a/libutil/reallocarray.c b/libutil/reallocarray.c index fb28267..c6e5219 100644 --- a/libutil/reallocarray.c +++ b/libutil/reallocarray.c @@ -1,4 +1,3 @@ -/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * diff --git a/ls.c b/ls.c index 44d430f..0099f5b 100644 --- a/ls.c +++ b/ls.c @@ -269,7 +269,7 @@ usage(void) int main(int argc, char *argv[]) { - struct entry *ents = NULL; + struct entry *ents; size_t i; ARGBEGIN { @@ -341,7 +341,7 @@ main(int argc, char *argv[]) if (argc == 0) *--argv = ".", argc++; - ents = ereallocarray(ents, argc, sizeof(*ents)); + ents = emallocarray(argc, sizeof(*ents)); for (i = 0; i < argc; i++) mkent(&ents[i], argv[i], 1, Hflag || Lflag); diff --git a/paste.c b/paste.c index 7a3878e..99046cc 100644 --- a/paste.c +++ b/paste.c @@ -85,8 +85,8 @@ usage(void) int main(int argc, char *argv[]) { - struct fdescr *dsc = NULL; - Rune *delim = NULL; + struct fdescr *dsc; + Rune *delim; size_t i, len; int seq = 0; char *adelim = "\t"; @@ -107,11 +107,11 @@ main(int argc, char *argv[]) /* populate delimiters */ unescape(adelim); - delim = ereallocarray(delim, utflen(adelim) + 1, sizeof(*delim)); + delim = emallocarray(utflen(adelim) + 1, sizeof(*delim)); len = utftorunestr(adelim, delim); /* populate file list */ - dsc = ereallocarray(dsc, argc, sizeof(*dsc)); + dsc = emallocarray(argc, sizeof(*dsc)); for (i = 0; i < argc; i++) { if (strcmp(argv[i], "-") == 0) diff --git a/printf.c b/printf.c index e959a46..656b808 100644 --- a/printf.c +++ b/printf.c @@ -111,7 +111,7 @@ main(int argc, char *argv[]) break; case 'c': unescape(arg); - rarg = ereallocarray(rarg, utflen(arg) + 1, sizeof(*rarg)); + rarg = emallocarray(utflen(arg) + 1, sizeof(*rarg)); utftorunestr(arg, rarg); efputrune(rarg, stdout, ""); free(rarg); @@ -125,7 +125,7 @@ main(int argc, char *argv[]) if (arg[j] == '\'' || arg[j] == '\"') { arg += j + 1; unescape(arg); - rarg = ereallocarray(rarg, utflen(arg) + 1, sizeof(*rarg)); + rarg = emallocarray(utflen(arg) + 1, sizeof(*rarg)); utftorunestr(arg, rarg); num = rarg[0]; } else diff --git a/sed.c b/sed.c index 8e98062..d413c71 100644 --- a/sed.c +++ b/sed.c @@ -596,10 +596,9 @@ chompr(char *s, Rune rune) Rune * strtorunes(char *s, size_t nrunes) { - Rune *rs = NULL, *rp; + Rune *rs, *rp; - rs = ereallocarray(rs, nrunes + 1, sizeof(*rs)); - rp = rs; + rp = rs = emallocarray(nrunes + 1, sizeof(*rs)); while (nrunes--) s += chartorune(rp++, s); diff --git a/strings.c b/strings.c index 8b16069..b1872eb 100644 --- a/strings.c +++ b/strings.c @@ -11,11 +11,11 @@ static char *format = ""; static void strings(FILE *fp, const char *fname, size_t len) { - Rune r, *rbuf = NULL; + Rune r, *rbuf; size_t i, bread; off_t off; - rbuf = ereallocarray(rbuf, len, sizeof(*rbuf)); + rbuf = emallocarray(len, sizeof(*rbuf)); for (off = 0, i = 0; (bread = efgetrune(&r, fp, fname)); ) { off += bread; diff --git a/tr.c b/tr.c index 52b223f..039df36 100644 --- a/tr.c +++ b/tr.c @@ -71,16 +71,16 @@ rstrmatch(Rune *r, char *s, size_t n) static size_t makeset(char *str, struct range **set, int (**check)(Rune)) { - Rune *rstr = NULL; + Rune *rstr; size_t len, i, j, m, n; size_t q, setranges = 0; int factor, base; /* rstr defines at most len ranges */ unescape(str); - rstr = ereallocarray(rstr, utflen(str) + 1, sizeof(*rstr)); + rstr = emallocarray(utflen(str) + 1, sizeof(*rstr)); len = utftorunestr(str, rstr); - *set = ereallocarray(*set, len, sizeof(**set)); + *set = emallocarray(len, sizeof(**set)); for (i = 0; i < len; i++) { if (rstr[i] == '[') { diff --git a/util.h b/util.h index 7370272..c4e7585 100644 --- a/util.h +++ b/util.h @@ -25,6 +25,8 @@ void apathmax(char **, size_t *); void *ecalloc(size_t, size_t); void *emalloc(size_t); +void *mallocarray(size_t, size_t); +void *emallocarray(size_t, size_t); void *erealloc(void *, size_t); void *reallocarray(void *, size_t, size_t); void *ereallocarray(void *, size_t, size_t);