aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjoern A. Zeeb <bz@FreeBSD.org>2013-02-19 13:27:20 +0000
committerBjoern A. Zeeb <bz@FreeBSD.org>2013-02-19 13:27:20 +0000
commit1c7fe7463d0204f08806b9da6d5d50fb7f75c5ad (patch)
tree227fb75a2a5f50439b8fc8344fcd41525e4fcd8f
parent97a8436b12801792594457d69cbb5a6e131755d9 (diff)
downloadsrc-releng/7.4.tar.gz
src-releng/7.4.zip
Fix Denial of Service vulnerability in named(8) with DNS64. [13:01]releng/7.4
Fix Denial of Service vulnerability in libc's glob(3) functionality. [13:02] Security: CVE-2012-5688 Security: FreeBSD-SA-13:01.bind Security: CVE-2010-2632 Security: FreeBSD-SA-13:02.libc Approved by: so (simon, bz)
Notes
Notes: svn path=/releng/7.4/; revision=246989
-rw-r--r--UPDATING3
-rw-r--r--lib/libc/gen/glob.c101
-rw-r--r--sys/conf/newvers.sh2
3 files changed, 83 insertions, 23 deletions
diff --git a/UPDATING b/UPDATING
index 92780d288147..78034e050786 100644
--- a/UPDATING
+++ b/UPDATING
@@ -8,6 +8,9 @@ Items affecting the ports and packages system can be found in
/usr/ports/UPDATING. Please read that file before running
portupgrade.
+20130218: p12 FreeBSD-SA-13:02.libc
+ Fix Denial of Service vulnerability in libc's glob(3) functionality.
+
20121122: p11 FreeBSD-SA-12:06.bind FreeBSD-SA-12:08.linux
Fix multiple Denial of Service vulnerabilities with named(8).
diff --git a/lib/libc/gen/glob.c b/lib/libc/gen/glob.c
index fa14394a50c1..b39d7ad6d005 100644
--- a/lib/libc/gen/glob.c
+++ b/lib/libc/gen/glob.c
@@ -89,6 +89,25 @@ __FBSDID("$FreeBSD$");
#include "collate.h"
+/*
+ * glob(3) expansion limits. Stop the expansion if any of these limits
+ * is reached. This caps the runtime in the face of DoS attacks. See
+ * also CVE-2010-2632
+ */
+#define GLOB_LIMIT_BRACE 128 /* number of brace calls */
+#define GLOB_LIMIT_PATH 65536 /* number of path elements */
+#define GLOB_LIMIT_READDIR 16384 /* number of readdirs */
+#define GLOB_LIMIT_STAT 1024 /* number of stat system calls */
+#define GLOB_LIMIT_STRING ARG_MAX /* maximum total size for paths */
+
+struct glob_limit {
+ size_t l_brace_cnt;
+ size_t l_path_lim;
+ size_t l_readdir_cnt;
+ size_t l_stat_cnt;
+ size_t l_string_cnt;
+};
+
#define DOLLAR '$'
#define DOT '.'
#define EOS '\0'
@@ -148,15 +167,18 @@ static Char *g_strchr(Char *, wchar_t);
static Char *g_strcat(Char *, const Char *);
#endif
static int g_stat(Char *, struct stat *, glob_t *);
-static int glob0(const Char *, glob_t *, size_t *);
-static int glob1(Char *, glob_t *, size_t *);
-static int glob2(Char *, Char *, Char *, Char *, glob_t *, size_t *);
-static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, size_t *);
-static int globextend(const Char *, glob_t *, size_t *);
-static const Char *
+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 *, Char *, glob_t *,
+ struct glob_limit *);
+static int glob3(Char *, Char *, Char *, Char *, 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 globexp2(const Char *, const Char *, glob_t *, int *, size_t *);
+static int globexp1(const Char *, glob_t *, struct glob_limit *);
+static int globexp2(const Char *, const Char *, glob_t *, int *,
+ struct glob_limit *);
static int match(Char *, Char *, Char *);
#ifdef DEBUG
static void qprintf(const char *, Char *);
@@ -165,8 +187,8 @@ static void qprintf(const char *, Char *);
int
glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob)
{
+ struct glob_limit limit = { 0, 0, 0, 0, 0 };
const char *patnext;
- size_t limit;
Char *bufnext, *bufend, patbuf[MAXPATHLEN], prot;
mbstate_t mbs;
wchar_t wc;
@@ -180,11 +202,10 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *
pglob->gl_offs = 0;
}
if (flags & GLOB_LIMIT) {
- limit = pglob->gl_matchc;
- if (limit == 0)
- limit = ARG_MAX;
- } else
- limit = 0;
+ limit.l_path_lim = pglob->gl_matchc;
+ if (limit.l_path_lim == 0)
+ limit.l_path_lim = GLOB_LIMIT_PATH;
+ }
pglob->gl_flags = flags & ~GLOB_MAGCHAR;
pglob->gl_errfunc = errfunc;
pglob->gl_matchc = 0;
@@ -237,11 +258,17 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *
* 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;
+ if ((pglob->gl_flags & GLOB_LIMIT) &&
+ limit->l_brace_cnt++ >= 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);
@@ -260,7 +287,8 @@ globexp1(const Char *pattern, glob_t *pglob, size_t *limit)
* If it fails then it tries to glob the rest of the pattern and returns.
*/
static int
-globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv, size_t *limit)
+globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv,
+ struct glob_limit *limit)
{
int i;
Char *lm, *ls;
@@ -430,7 +458,7 @@ globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob)
* if things went well, nonzero if errors occurred.
*/
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 err;
@@ -523,7 +551,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];
@@ -541,7 +569,7 @@ glob1(Char *pattern, glob_t *pglob, size_t *limit)
*/
static int
glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern,
- glob_t *pglob, size_t *limit)
+ glob_t *pglob, struct glob_limit *limit)
{
struct stat sb;
Char *p, *q;
@@ -557,6 +585,15 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern,
if (g_lstat(pathbuf, &sb, pglob))
return(0);
+ if ((pglob->gl_flags & GLOB_LIMIT) &&
+ limit->l_stat_cnt++ >= GLOB_LIMIT_STAT) {
+ errno = 0;
+ if (pathend + 1 > pathend_last)
+ return (GLOB_ABORTED);
+ *pathend++ = SEP;
+ *pathend = EOS;
+ return (GLOB_NOSPACE);
+ }
if (((pglob->gl_flags & GLOB_MARK) &&
pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
|| (S_ISLNK(sb.st_mode) &&
@@ -600,7 +637,7 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern,
static int
glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
Char *pattern, Char *restpattern,
- glob_t *pglob, size_t *limit)
+ glob_t *pglob, struct glob_limit *limit)
{
struct dirent *dp;
DIR *dirp;
@@ -646,6 +683,19 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
size_t clen;
mbstate_t mbs;
+ if ((pglob->gl_flags & GLOB_LIMIT) &&
+ limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) {
+ errno = 0;
+ if (pathend + 1 > pathend_last)
+ err = GLOB_ABORTED;
+ else {
+ *pathend++ = SEP;
+ *pathend = EOS;
+ err = GLOB_NOSPACE;
+ }
+ break;
+ }
+
/* Initial DOT must be matched literally. */
if (dp->d_name[0] == DOT && *pattern != DOT)
continue;
@@ -696,14 +746,15 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
* 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;
char *copy;
const Char *p;
- if (*limit && pglob->gl_pathc > *limit) {
+ if ((pglob->gl_flags & GLOB_LIMIT) &&
+ pglob->gl_matchc > limit->l_path_lim) {
errno = 0;
return (GLOB_NOSPACE);
}
@@ -731,6 +782,12 @@ globextend(const Char *path, glob_t *pglob, size_t *limit)
for (p = path; *p++;)
continue;
len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */
+ limit->l_string_cnt += len;
+ if ((pglob->gl_flags & GLOB_LIMIT) &&
+ limit->l_string_cnt >= GLOB_LIMIT_STRING) {
+ errno = 0;
+ return (GLOB_NOSPACE);
+ }
if ((copy = malloc(len)) != NULL) {
if (g_Ctoc(path, copy, len)) {
free(copy);
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index 00cf19e9d736..dfb697860abd 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -32,7 +32,7 @@
TYPE="FreeBSD"
REVISION="7.4"
-BRANCH="RELEASE-p11"
+BRANCH="RELEASE-p12"
if [ "X${BRANCH_OVERRIDE}" != "X" ]; then
BRANCH=${BRANCH_OVERRIDE}
fi