sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

commit 3c33abc520d90bd1a0712a6406cd750b560de053
parent 3b825735d8ad2645bafac8269e4078349dc1d949
Author: FRIGN <dev@frign.de>
Date:   Tue Mar 10 22:19:19 +0100

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.

Diffstat:
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(-)
diff --git 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 @@ -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 @@ -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 @@ -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 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008 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 <sys/types.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> + +#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 @@ -1,4 +1,3 @@ -/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> * diff --git 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 @@ -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 @@ -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, "<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 @@ -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 @@ -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 @@ -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 @@ -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);