From e6d36a6ae6af7586981fae0e30d428c549c34c1a Mon Sep 17 00:00:00 2001 From: christos Date: Fri, 21 Jan 2011 23:30:31 +0000 Subject: [PATCH] prevent resource DoS from brace expansion (from Maksymilian Arciemowicz) --- lib/libc/gen/glob.c | 81 +++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/lib/libc/gen/glob.c b/lib/libc/gen/glob.c index 9d7d60797b88..c1083f90b2f8 100644 --- a/lib/libc/gen/glob.c +++ b/lib/libc/gen/glob.c @@ -1,4 +1,4 @@ -/* $NetBSD: glob.c,v 1.27 2010/09/06 14:40:25 christos Exp $ */ +/* $NetBSD: glob.c,v 1.28 2011/01/21 23:30:31 christos Exp $ */ /* * Copyright (c) 1989, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93"; #else -__RCSID("$NetBSD: glob.c,v 1.27 2010/09/06 14:40:25 christos Exp $"); +__RCSID("$NetBSD: glob.c,v 1.28 2011/01/21 23:30:31 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -87,13 +87,18 @@ __RCSID("$NetBSD: glob.c,v 1.27 2010/09/06 14:40:25 christos Exp $"); #define NO_GETPW_R #endif -#define GLOB_LIMIT_MALLOC 65536 -#define GLOB_LIMIT_STAT 128 -#define GLOB_LIMIT_READDIR 16384 +#define GLOB_LIMIT_STRING 65536 /* number of readdirs */ +#define GLOB_LIMIT_STAT 128 /* number of stat system calls */ +#define GLOB_LIMIT_READDIR 16384 /* total buffer size of path strings */ +#define GLOB_LIMIT_PATH 1024 /* number of path elements */ +#define GLOB_LIMIT_BRACE 128 /* Number of brace calls */ -#define GLOB_INDEX_MALLOC 0 -#define GLOB_INDEX_STAT 1 -#define GLOB_INDEX_READDIR 2 +struct glob_limit { + size_t l_string; + size_t l_stat; + size_t l_readdir; + size_t l_brace; +}; /* * XXX: For NetBSD 1.4.x compatibility. (kill me l8r) @@ -158,17 +163,17 @@ static int g_lstat(Char *, __gl_stat_t *, glob_t *); static DIR *g_opendir(Char *, glob_t *); static Char *g_strchr(const Char *, int); static int g_stat(Char *, __gl_stat_t *, glob_t *); -static int glob0(const Char *, glob_t *, size_t *); -static int glob1(Char *, glob_t *, size_t *); +static int glob0(const Char *, glob_t *, struct glob_limit *); +static int glob1(Char *, glob_t *, struct glob_limit *); static int glob2(Char *, Char *, Char *, const Char *, glob_t *, - size_t *); + struct glob_limit *); static int glob3(Char *, Char *, Char *, const Char *, const Char *, - const Char *, glob_t *, size_t *); -static int globextend(const Char *, glob_t *, size_t *); + const Char *, glob_t *, struct glob_limit *); +static int globextend(const Char *, glob_t *, struct glob_limit *); static const Char *globtilde(const Char *, Char *, size_t, glob_t *); -static int globexp1(const Char *, glob_t *, size_t *); +static int globexp1(const Char *, glob_t *, struct glob_limit *); static int globexp2(const Char *, const Char *, glob_t *, int *, - size_t *); + struct glob_limit *); static int match(const Char *, const Char *, const Char *); #ifdef DEBUG static void qprintf(const char *, Char *); @@ -181,8 +186,7 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), const u_char *patnext; int c; Char *bufnext, *bufend, patbuf[MAXPATHLEN+1]; - /* 0 = malloc(), 1 = stat(), 2 = readdir() */ - size_t limit[] = { 0, 0, 0 }; + struct glob_limit limit = { 0, 0, 0, 0 }; _DIAGASSERT(pattern != NULL); @@ -218,9 +222,9 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), *bufnext = EOS; if (flags & GLOB_BRACE) - return globexp1(patbuf, pglob, limit); + return globexp1(patbuf, pglob, &limit); else - return glob0(patbuf, pglob, limit); + return glob0(patbuf, pglob, &limit); } /* @@ -229,7 +233,7 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), * characters */ static int -globexp1(const Char *pattern, glob_t *pglob, size_t *limit) +globexp1(const Char *pattern, glob_t *pglob, struct glob_limit *limit) { const Char* ptr = pattern; int rv; @@ -237,6 +241,12 @@ globexp1(const Char *pattern, glob_t *pglob, size_t *limit) _DIAGASSERT(pattern != NULL); _DIAGASSERT(pglob != NULL); + if ((pglob->gl_flags & GLOB_LIMIT) && + limit->l_brace++ >= GLOB_LIMIT_BRACE) { + errno = 0; + return GLOB_NOSPACE; + } + /* Protect a single {}, for find(1), like csh */ if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) return glob0(pattern, pglob, limit); @@ -256,7 +266,7 @@ globexp1(const Char *pattern, glob_t *pglob, size_t *limit) */ static int globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv, - size_t *limit) + struct glob_limit *limit) { int i; Char *lm, *ls; @@ -461,7 +471,7 @@ globtilde(const Char *pattern, Char *patbuf, size_t patsize, glob_t *pglob) * to find no matches. */ static int -glob0(const Char *pattern, glob_t *pglob, size_t *limit) +glob0(const Char *pattern, glob_t *pglob, struct glob_limit *limit) { const Char *qpatnext; int c, error; @@ -570,7 +580,7 @@ compare(const void *p, const void *q) } static int -glob1(Char *pattern, glob_t *pglob, size_t *limit) +glob1(Char *pattern, glob_t *pglob, struct glob_limit *limit) { Char pathbuf[MAXPATHLEN+1]; @@ -596,7 +606,7 @@ glob1(Char *pattern, glob_t *pglob, size_t *limit) */ static int glob2(Char *pathbuf, Char *pathend, Char *pathlim, const Char *pattern, - glob_t *pglob, size_t *limit) + glob_t *pglob, struct glob_limit *limit) { __gl_stat_t sb; const Char *p; @@ -624,10 +634,11 @@ glob2(Char *pathbuf, Char *pathend, Char *pathlim, const Char *pattern, return 0; if ((pglob->gl_flags & GLOB_LIMIT) && - limit[GLOB_INDEX_STAT]++ >= GLOB_LIMIT_STAT) { + limit->l_stat++ >= GLOB_LIMIT_STAT) { errno = 0; *pathend++ = SEP; *pathend = EOS; +printf("stat limit\n"); return GLOB_NOSPACE; } if (((pglob->gl_flags & GLOB_MARK) && @@ -692,7 +703,7 @@ glob2(Char *pathbuf, Char *pathend, Char *pathlim, const Char *pattern, static int glob3(Char *pathbuf, Char *pathend, Char *pathlim, const Char *pattern, const Char *restpattern, const Char *pglobstar, glob_t *pglob, - size_t *limit) + struct glob_limit *limit) { struct dirent *dp; DIR *dirp; @@ -785,7 +796,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pathlim, const Char *pattern, Char *dc; if ((pglob->gl_flags & GLOB_LIMIT) && - limit[GLOB_INDEX_READDIR]++ >= GLOB_LIMIT_READDIR) { + limit->l_readdir++ >= GLOB_LIMIT_READDIR) { errno = 0; *pathend++ = SEP; *pathend = EOS; @@ -894,7 +905,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pathlim, const Char *pattern, * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int -globextend(const Char *path, glob_t *pglob, size_t *limit) +globextend(const Char *path, glob_t *pglob, struct glob_limit *limit) { char **pathv; size_t i, newsize, len; @@ -905,6 +916,9 @@ globextend(const Char *path, glob_t *pglob, size_t *limit) _DIAGASSERT(pglob != NULL); newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); + if ((pglob->gl_flags & GLOB_LIMIT) && + newsize > GLOB_LIMIT_PATH * sizeof(*pathv)) + goto nospace; pathv = pglob->gl_pathv ? realloc(pglob->gl_pathv, newsize) : malloc(newsize); if (pathv == NULL) @@ -921,7 +935,7 @@ globextend(const Char *path, glob_t *pglob, size_t *limit) for (p = path; *p++;) continue; len = (size_t)(p - path); - limit[GLOB_INDEX_MALLOC] += len; + limit->l_string += len; if ((copy = malloc(len)) != NULL) { if (g_Ctoc(path, copy, len)) { free(copy); @@ -932,12 +946,13 @@ globextend(const Char *path, glob_t *pglob, size_t *limit) pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; if ((pglob->gl_flags & GLOB_LIMIT) && - (newsize + limit[GLOB_INDEX_MALLOC]) >= GLOB_LIMIT_MALLOC) { - errno = 0; - return GLOB_NOSPACE; - } + (newsize + limit->l_string) >= GLOB_LIMIT_STRING) + goto nospace; return copy == NULL ? GLOB_NOSPACE : 0; +nospace: + errno = 0; + return GLOB_NOSPACE; }