From nobody@FreeBSD.org  Thu Jun 21 20:27:19 2012
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id DFBB8106564A
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 21 Jun 2012 20:27:19 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22])
	by mx1.freebsd.org (Postfix) with ESMTP id CA8DF8FC0C
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 21 Jun 2012 20:27:19 +0000 (UTC)
Received: from red.freebsd.org (localhost [127.0.0.1])
	by red.freebsd.org (8.14.4/8.14.4) with ESMTP id q5LKRJn6065729
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 21 Jun 2012 20:27:19 GMT
	(envelope-from nobody@red.freebsd.org)
Received: (from nobody@localhost)
	by red.freebsd.org (8.14.4/8.14.4/Submit) id q5LKRJwH065728;
	Thu, 21 Jun 2012 20:27:19 GMT
	(envelope-from nobody)
Message-Id: <201206212027.q5LKRJwH065728@red.freebsd.org>
Date: Thu, 21 Jun 2012 20:27:19 GMT
From: Zak Blacher <zblacher@sandvine.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: Applied MidnightBSD regex memory consumption limits
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         169302
>Category:       kern
>Synopsis:       [libc] [patch] Applied MidnightBSD regex memory consumption limits
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jun 21 20:30:14 UTC 2012
>Closed-Date:    
>Last-Modified:  Mon May 05 16:44:33 UTC 2014
>Originator:     Zak Blacher
>Release:        8.2
>Organization:
Sandvine Inc.
>Environment:
FreeBSD hostname.xxxxxxxx 8.82.0213-RELEASE-p5 FreeBSD 8.82.0213-RELEASE-p5 #124: Tue Jun 19 10:21:28 EDT 2012 root@hostname.xxxxxxxx:/path/ amd64
>Description:
re: http://seclists.org/fulldisclosure/2011/Nov/53

Applied MidnightBSD patch to regex library. Asserts an upper limit on memory and recursion depth one regular expression can consume. 
>How-To-Repeat:

>Fix:


Patch attached with submission follows:

--- src/lib/libc/regex/regcomp.c        2008/10/30 20:39:06     1.1.1.2
+++ src/lib/libc/regex/regcomp.c        2011/11/04 17:42:29     1.3
@@ -81,11 +81,11 @@ extern "C" {
 #endif

 /* === regcomp.c === */
-static void p_ere(struct parse *p, wint_t stop);
-static void p_ere_exp(struct parse *p);
+static void p_ere(struct parse *p, wint_t stop, size_t reclimit);
+static void p_ere_exp(struct parse *p, size_t reclimit);
 static void p_str(struct parse *p);
-static void p_bre(struct parse *p, wint_t end1, wint_t end2);
-static int p_simp_re(struct parse *p, int starordinary);
+static void p_bre(struct parse *p, wint_t end1, wint_t end2, size_t reclimit);
+static int p_simp_re(struct parse *p, int starordinary, size_t reclimit);
 static int p_count(struct parse *p);
 static void p_bracket(struct parse *p);
 static void p_b_term(struct parse *p, cset *cs);
@@ -97,7 +97,7 @@ static wint_t othercase(wint_t ch);
 static void bothcases(struct parse *p, wint_t ch);
 static void ordinary(struct parse *p, wint_t ch);
 static void nonnewline(struct parse *p);
-static void repeat(struct parse *p, sopno start, int from, int to);
+static void repeat(struct parse *p, sopno start, int from, int to, size_t reclimit);
 static int seterr(struct parse *p, int e);
 static cset *allocset(struct parse *p);
 static void freeset(struct parse *p, cset *cs);
@@ -109,7 +109,7 @@ static sopno dupl(struct parse *p, sopno
 static void doemit(struct parse *p, sop op, size_t opnd);
 static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos);
 static void dofwd(struct parse *p, sopno pos, sop value);
-static void enlarge(struct parse *p, sopno size);
+static int enlarge(struct parse *p, sopno size);
 static void stripsnug(struct parse *p, struct re_guts *g);
 static void findmust(struct parse *p, struct re_guts *g);
 static int altoffset(sop *scan, int offset);
@@ -162,6 +162,13 @@ static int never = 0;              /* for use in ass
 #define        never   0               /* some <assert.h>s have bugs too */
 #endif

+#define MEMLIMIT    0x8000000
+#define MEMSIZE(p) \
+((p)->ncsalloc / CHAR_BIT + \
+(p)->ncsalloc * sizeof(cset) + \
+(p)->ssize * sizeof(sop))
+#define RECLIMIT    256
+
 /* Macro used by computejump()/computematchjump() */
 #define MIN(a,b)       ((a)<(b)?(a):(b))

@@ -244,11 +251,11 @@ regcomp(regex_t * __restrict preg,
        EMIT(OEND, 0);
        g->firststate = THERE();
        if (cflags&REG_EXTENDED)
-               p_ere(p, OUT);
+               p_ere(p, OUT, 0);
        else if (cflags&REG_NOSPEC)
                p_str(p);
        else
-               p_bre(p, OUT, OUT);
+               p_bre(p, OUT, OUT, 0);
        EMIT(OEND, 0);
        g->laststate = THERE();

@@ -289,19 +296,25 @@ regcomp(regex_t * __restrict preg,
  */
 static void
 p_ere(struct parse *p,
-       int stop)               /* character this ERE should end at */
+       int stop,               /* character this ERE should end at */
+    size_t reclimit)
 {
        char c;
        sopno prevback;
        sopno prevfwd;
        sopno conc;
        int first = 1;          /* is this the first alternative? */
+
+    if (reclimit++ > RECLIMIT || p->error == REG_ESPACE) {
+        p->error = REG_ESPACE;
+        return;
+    }

        for (;;) {
                /* do a bunch of concatenated expressions */
                conc = HERE();
                while (MORE() && (c = PEEK()) != '|' && c != stop)
-                       p_ere_exp(p);
+                       p_ere_exp(p, reclimit);
                (void)REQUIRE(HERE() != conc, REG_EMPTY);       /* require nonempty */

                if (!EAT('|'))
@@ -333,7 +346,7 @@ p_ere(struct parse *p,
  == static void p_ere_exp(struct parse *p);
  */
 static void
-p_ere_exp(struct parse *p)
+p_ere_exp(struct parse *p, size_t reclimit)
 {
        char c;
        wint_t wc;
@@ -356,7 +369,7 @@ p_ere_exp(struct parse *p)
                        p->pbegin[subno] = HERE();
                EMIT(OLPAREN, subno);
                if (!SEE(')'))
-                       p_ere(p, ')');
+                       p_ere(p, ')', reclimit);
                if (subno < NPAREN) {
                        p->pend[subno] = HERE();
                        assert(p->pend[subno] != 0);
@@ -460,7 +473,7 @@ p_ere_exp(struct parse *p)
                                count2 = INFINITY;
                } else          /* just a single number */
                        count2 = count;
-               repeat(p, pos, count, count2);
+               repeat(p, pos, count, count2, reclimit);
                if (!EAT('}')) {        /* error heuristics */
                        while (MORE() && PEEK() != '}')
                                NEXT();
@@ -504,8 +517,15 @@ p_str(struct parse *p)
 static void
 p_bre(struct parse *p,
        int end1,               /* first terminating character */
-       int end2)               /* second terminating character */
+       int end2,               /* second terminating character */
+       size_t reclimit)
 {
+
+       if (reclimit++ > RECLIMIT || p->error == REG_ESPACE) {
+               p->error = REG_ESPACE;
+               return;
+       }
+
        sopno start = HERE();
        int first = 1;                  /* first subexpression? */
        int wasdollar = 0;
@@ -516,7 +536,7 @@ p_bre(struct parse *p,
                p->g->nbol++;
        }
        while (MORE() && !SEETWO(end1, end2)) {
-               wasdollar = p_simp_re(p, first);
+               wasdollar = p_simp_re(p, first, reclimit);
                first = 0;
        }
        if (wasdollar) {        /* oops, that was a trailing anchor */
@@ -535,7 +555,8 @@ p_bre(struct parse *p,
  */
 static int                     /* was the simple RE an unbackslashed $? */
 p_simp_re(struct parse *p,
-       int starordinary)       /* is a leading * an ordinary character? */
+       int starordinary,       /* is a leading * an ordinary character? */
+       size_t reclimit)
 {
        int c;
        int count;
@@ -575,7 +596,7 @@ p_simp_re(struct parse *p,
                EMIT(OLPAREN, subno);
                /* the MORE here is an error heuristic */
                if (MORE() && !SEETWO('\\', ')'))
-                       p_bre(p, '\\', ')');
+                       p_bre(p, '\\', ')', reclimit);
                if (subno < NPAREN) {
                        p->pend[subno] = HERE();
                        assert(p->pend[subno] != 0);
@@ -636,7 +657,7 @@ p_simp_re(struct parse *p,
                                count2 = INFINITY;
                } else          /* just a single number */
                        count2 = count;
-               repeat(p, pos, count, count2);
+               repeat(p, pos, count, count2, reclimit);
                if (!EATTWO('\\', '}')) {       /* error heuristics */
                        while (MORE() && !SEETWO('\\', '}'))
                                NEXT();
@@ -995,7 +1016,8 @@ static void
 repeat(struct parse *p,
        sopno start,            /* operand from here to end of strip */
        int from,               /* repeated from this number */
-       int to)                 /* to this number of times (maybe INFINITY) */
+       int to,                 /* to this number of times (maybe INFINITY) */
+       size_t reclimit)
 {
        sopno finish = HERE();
 #      define  N       2
@@ -1018,7 +1040,7 @@ repeat(struct parse *p,
        case REP(0, INF):               /* as x{1,}? */
                /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
                INSERT(OCH_, start);            /* offset is wrong... */
-               repeat(p, start+1, 1, to);
+               repeat(p, start+1, 1, to, reclimit);
                ASTERN(OOR1, start);
                AHEAD(start);                   /* ... fix it */
                EMIT(OOR2, 0);
@@ -1038,7 +1060,7 @@ repeat(struct parse *p,
                ASTERN(O_CH, THERETHERE());
                copy = dupl(p, start+1, finish+1);
                assert(copy == finish+4);
-               repeat(p, copy, 1, to-1);
+               repeat(p, copy, 1, to-1, reclimit);
                break;
        case REP(1, INF):               /* as x+ */
                INSERT(OPLUS_, start);
@@ -1046,11 +1068,11 @@ repeat(struct parse *p,
                break;
        case REP(N, N):                 /* as xx{m-1,n-1} */
                copy = dupl(p, start, finish);
-               repeat(p, copy, from-1, to-1);
+               repeat(p, copy, from-1, to-1, reclimit);
                break;
        case REP(N, INF):               /* as xx{n-1,INF} */
                copy = dupl(p, start, finish);
-               repeat(p, copy, from-1, to);
+               repeat(p, copy, from-1, to, reclimit);
                break;
        default:                        /* "can't happen" */
                SETERROR(REG_ASSERT);   /* just in case */
@@ -1104,9 +1126,13 @@ static cset *
 allocset(struct parse *p)
 {
        cset *cs, *ncs;
+
+       if (MEMSIZE(p) > MEMLIMIT)
+               goto oomem;

        ncs = realloc(p->g->sets, (p->g->ncsets + 1) * sizeof(*ncs));
        if (ncs == NULL) {
+       oomem:
                SETERROR(REG_ESPACE);
                return (NULL);
        }
@@ -1246,8 +1272,8 @@ dupl(struct parse *p,
        assert(finish >= start);
        if (len == 0)
                return(ret);
-       enlarge(p, p->ssize + len);     /* this many unexpected additions */
-       assert(p->ssize >= p->slen + len);
+       if (!enlarge(p, p->ssize + len))        /* this many unexpected additions */
+               return ret;
        (void) memcpy((char *)(p->strip + p->slen),
                (char *)(p->strip + start), (size_t)len*sizeof(sop));
        p->slen += len;
@@ -1274,8 +1300,8 @@ doemit(struct parse *p, sop op, size_t o

        /* deal with undersized strip */
        if (p->slen >= p->ssize)
-               enlarge(p, (p->ssize+1) / 2 * 3);       /* +50% */
-       assert(p->slen < p->ssize);
+               if (!enlarge(p, (p->ssize+1) / 2 * 3))  /* +50% */
+                       return;

        /* finally, it's all reduced to the easy case */
        p->strip[p->slen++] = SOP(op, opnd);
@@ -1336,21 +1362,27 @@ dofwd(struct parse *p, sopno pos, sop va
  - enlarge - enlarge the strip
  == static void enlarge(struct parse *p, sopno size);
  */
-static void
+static int
 enlarge(struct parse *p, sopno size)
 {
        sop *sp;
+       sopno osize;

        if (p->ssize >= size)
-               return;
-
+               return 1;
+       osize = p->ssize;
+       p->ssize = size;
+       if (MEMSIZE(p) > MEMLIMIT)
+               goto oomem;
        sp = (sop *)realloc(p->strip, size*sizeof(sop));
        if (sp == NULL) {
+oomem:
+               p->ssize = osize;
                SETERROR(REG_ESPACE);
-               return;
+               return 0;
        }
        p->strip = sp;
-       p->ssize = size;
+       return 1;
 }

 /*


>Release-Note:
>Audit-Trail:

From: Pedro Giffuni <pfg@freebsd.org>
To: "bug-followup@FreeBSD.org" <bug-followup@FreeBSD.org>,
  "zblacher@sandvine.com" <zblacher@sandvine.com>
Cc:  
Subject: Re: kern/169302: [libc] [patch] Applied MidnightBSD regex memory consumption limits
Date: Sat, 3 May 2014 19:54:04 -0700 (PDT)

 ---1130188905-1388205096-1399172044=:87984
 Content-Type: text/plain; charset=utf-8
 Content-Transfer-Encoding: quoted-printable
 
 Nice! The patch doesn't apply cleanly but it seems related to this commit
  in NetBSD:  "Prevent regcomp/regexec DoS attacks by limiting the amount
 of memory used and the level of recursion. Thanks to Maksymilian Arciem=
 owicz for discovery and help with the implementation." 
 http://cvsweb=.netbsd.org/bsdweb.cgi/src/lib/libc/regex/regcomp.c.diff?r1=1.29&r2=1.30&only_with_tag=MAIN

 ---1130188905-1388205096-1399172044=:87984--

From: Pedro Giffuni <pfg@freebsd.org>
To: "bug-followup@FreeBSD.org" <bug-followup@FreeBSD.org>, 
 "zblacher@sandvine.com" <zblacher@sandvine.com>
Cc:  
Subject: Re: kern/169302: [libc] [patch] Applied MidnightBSD regex memory
 consumption limits
Date: Sun, 04 May 2014 11:41:45 -0500

 This is a multi-part message in MIME format.
 --------------020708080805040202010709
 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
 Content-Transfer-Encoding: 7bit
 
 Updated patch, based on the NetBSD changes.
 
 --------------020708080805040202010709
 Content-Type: text/plain; charset=us-ascii;
  name="patch-regex-pr169302.txt"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="patch-regex-pr169302.txt"
 
 Index: lib/libc/regex/engine.c
 ===================================================================
 --- lib/libc/regex/engine.c	(revision 265307)
 +++ lib/libc/regex/engine.c	(working copy)
 @@ -219,7 +219,7 @@
  		} else {
  			for (dp = start; dp < stop; dp++)
  				if (*dp == g->must[0] &&
 -				    stop - dp >= g->mlen &&
 +				    (size_t)(stop - dp) >= g->mlen &&
  				    memcmp(dp, g->must, (size_t)g->mlen) == 0)
  					break;
  			if (dp == stop)		/* we didn't find g->must */
 Index: lib/libc/regex/regcomp.c
 ===================================================================
 --- lib/libc/regex/regcomp.c	(revision 265307)
 +++ lib/libc/regex/regcomp.c	(working copy)
 @@ -86,11 +86,11 @@
  #endif
  
  /* === regcomp.c === */
 -static void p_ere(struct parse *p, int stop);
 -static void p_ere_exp(struct parse *p);
 +static void p_ere(struct parse *p, int stop, size_t reclimit);
 +static void p_ere_exp(struct parse *p, size_t reclimit);
  static void p_str(struct parse *p);
 -static void p_bre(struct parse *p, int end1, int end2);
 -static int p_simp_re(struct parse *p, int starordinary);
 +static void p_bre(struct parse *p, int end1, int end2, size_t reclimit);
 +static int p_simp_re(struct parse *p, int starordinary, size_t reclimit);
  static int p_count(struct parse *p);
  static void p_bracket(struct parse *p);
  static void p_b_term(struct parse *p, cset *cs);
 @@ -102,7 +102,7 @@
  static void bothcases(struct parse *p, wint_t ch);
  static void ordinary(struct parse *p, wint_t ch);
  static void nonnewline(struct parse *p);
 -static void repeat(struct parse *p, sopno start, int from, int to);
 +static void repeat(struct parse *p, sopno start, int from, int to, size_t reclimit);
  static int seterr(struct parse *p, int e);
  static cset *allocset(struct parse *p);
  static void freeset(struct parse *p, cset *cs);
 @@ -167,6 +167,13 @@
  #define	never	0		/* some <assert.h>s have bugs too */
  #endif
  
 +#define	MEMLIMIT	0x8000000
 +#define MEMSIZE(p) \
 +	((p)->ncsalloc / CHAR_BIT * NC + \
 +	(p)->ncsalloc * sizeof(cset) + \
 +	(p)->ssize * sizeof(sop))
 +#define	RECLIMIT	256
 +
  /* Macro used by computejump()/computematchjump() */
  #define MIN(a,b)	((a)<(b)?(a):(b))
  
 @@ -249,11 +256,11 @@
  	EMIT(OEND, 0);
  	g->firststate = THERE();
  	if (cflags&REG_EXTENDED)
 -		p_ere(p, OUT);
 +		p_ere(p, OUT, 0);
  	else if (cflags&REG_NOSPEC)
  		p_str(p);
  	else
 -		p_bre(p, OUT, OUT);
 +		p_bre(p, OUT, OUT, 0);
  	EMIT(OEND, 0);
  	g->laststate = THERE();
  
 @@ -294,7 +301,8 @@
   */
  static void
  p_ere(struct parse *p,
 -	int stop)		/* character this ERE should end at */
 +	int stop,		/* character this ERE should end at */
 +	size_t reclimit)
  {
  	char c;
  	sopno prevback;
 @@ -302,11 +310,16 @@
  	sopno conc;
  	int first = 1;		/* is this the first alternative? */
  
 +	if (reclimit++ > RECLIMIT || p->error == REG_ESPACE) {
 +		p->error = REG_ESPACE;
 +		return;
 +	}
 +
  	for (;;) {
  		/* do a bunch of concatenated expressions */
  		conc = HERE();
  		while (MORE() && (c = PEEK()) != '|' && c != stop)
 -			p_ere_exp(p);
 +			p_ere_exp(p, reclimit);
  		(void)REQUIRE(HERE() != conc, REG_EMPTY);	/* require nonempty */
  
  		if (!EAT('|'))
 @@ -335,10 +348,10 @@
  
  /*
   - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op
 - == static void p_ere_exp(struct parse *p);
 + == static void p_ere_exp(struct parse *p, size_t reclimit);
   */
  static void
 -p_ere_exp(struct parse *p)
 +p_ere_exp(struct parse *p, size_t reclimit)
  {
  	char c;
  	wint_t wc;
 @@ -361,7 +374,7 @@
  			p->pbegin[subno] = HERE();
  		EMIT(OLPAREN, subno);
  		if (!SEE(')'))
 -			p_ere(p, ')');
 +			p_ere(p, ')', reclimit);
  		if (subno < NPAREN) {
  			p->pend[subno] = HERE();
  			assert(p->pend[subno] != 0);
 @@ -465,7 +478,7 @@
  				count2 = INFINITY;
  		} else		/* just a single number */
  			count2 = count;
 -		repeat(p, pos, count, count2);
 +		repeat(p, pos, count, count2, 0);
  		if (!EAT('}')) {	/* error heuristics */
  			while (MORE() && PEEK() != '}')
  				NEXT();
 @@ -499,7 +512,7 @@
  /*
   - p_bre - BRE parser top level, anchoring and concatenation
   == static void p_bre(struct parse *p,  int end1, \
 - ==	int end2);
 + ==	int end2, size_t reclimit);
   * Giving end1 as OUT essentially eliminates the end1/end2 check.
   *
   * This implementation is a bit of a kludge, in that a trailing $ is first
 @@ -509,8 +522,14 @@
  static void
  p_bre(struct parse *p,
  	int end1,		/* first terminating character */
 -	int end2)		/* second terminating character */
 +	int end2,		/* second terminating character */
 +	size_t reclimit)
  {
 +	if (reclimit++ > RECLIMIT || p->error == REG_ESPACE) {
 +		p->error = REG_ESPACE;
 +		return;
 +	}
 +
  	sopno start = HERE();
  	int first = 1;			/* first subexpression? */
  	int wasdollar = 0;
 @@ -521,7 +540,7 @@
  		p->g->nbol++;
  	}
  	while (MORE() && !SEETWO(end1, end2)) {
 -		wasdollar = p_simp_re(p, first);
 +		wasdollar = p_simp_re(p, first, reclimit);
  		first = 0;
  	}
  	if (wasdollar) {	/* oops, that was a trailing anchor */
 @@ -536,11 +555,12 @@
  
  /*
   - p_simp_re - parse a simple RE, an atom possibly followed by a repetition
 - == static int p_simp_re(struct parse *p, int starordinary);
 + == static int p_simp_re(struct parse *p, int starordinary, size_t reclimit);
   */
  static int			/* was the simple RE an unbackslashed $? */
  p_simp_re(struct parse *p,
 -	int starordinary)	/* is a leading * an ordinary character? */
 +	int starordinary,	/* is a leading * an ordinary character? */
 +	size_t reclimit)
  {
  	int c;
  	int count;
 @@ -580,7 +600,7 @@
  		EMIT(OLPAREN, subno);
  		/* the MORE here is an error heuristic */
  		if (MORE() && !SEETWO('\\', ')'))
 -			p_bre(p, '\\', ')');
 +			p_bre(p, '\\', ')', reclimit);
  		if (subno < NPAREN) {
  			p->pend[subno] = HERE();
  			assert(p->pend[subno] != 0);
 @@ -641,7 +661,7 @@
  				count2 = INFINITY;
  		} else		/* just a single number */
  			count2 = count;
 -		repeat(p, pos, count, count2);
 +		repeat(p, pos, count, count2, 0);
  		if (!EATTWO('\\', '}')) {	/* error heuristics */
  			while (MORE() && !SEETWO('\\', '}'))
  				NEXT();
 @@ -996,13 +1016,15 @@
  
  /*
   - repeat - generate code for a bounded repetition, recursively if needed
 - == static void repeat(struct parse *p, sopno start, int from, int to);
 + == static void repeat(struct parse *p, sopno start, int from, int to,
 + == size_t reclimit );
   */
  static void
  repeat(struct parse *p,
  	sopno start,		/* operand from here to end of strip */
  	int from,		/* repeated from this number */
 -	int to)			/* to this number of times (maybe INFINITY) */
 +	int to,			/* to this number of times (maybe INFINITY) */
 +	size_t reclimit)
  {
  	sopno finish = HERE();
  #	define	N	2
 @@ -1011,7 +1033,9 @@
  #	define	MAP(n)	(((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N)
  	sopno copy;
  
 -	if (p->error != 0)	/* head off possible runaway recursion */
 +	if (reclimit++ > RECLIMIT) 
 +		p->error = REG_ESPACE;
 +	if (p->error)
  		return;
  
  	assert(from <= to);
 @@ -1025,7 +1049,7 @@
  	case REP(0, INF):		/* as x{1,}? */
  		/* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
  		INSERT(OCH_, start);		/* offset is wrong... */
 -		repeat(p, start+1, 1, to);
 +		repeat(p, start+1, 1, to, reclimit);
  		ASTERN(OOR1, start);
  		AHEAD(start);			/* ... fix it */
  		EMIT(OOR2, 0);
 @@ -1045,7 +1069,7 @@
  		ASTERN(O_CH, THERETHERE());
  		copy = dupl(p, start+1, finish+1);
  		assert(copy == finish+4);
 -		repeat(p, copy, 1, to-1);
 +		repeat(p, copy, 1, to-1, reclimit);
  		break;
  	case REP(1, INF):		/* as x+ */
  		INSERT(OPLUS_, start);
 @@ -1053,11 +1077,11 @@
  		break;
  	case REP(N, N):			/* as xx{m-1,n-1} */
  		copy = dupl(p, start, finish);
 -		repeat(p, copy, from-1, to-1);
 +		repeat(p, copy, from-1, to-1, reclimit);
  		break;
  	case REP(N, INF):		/* as xx{n-1,INF} */
  		copy = dupl(p, start, finish);
 -		repeat(p, copy, from-1, to);
 +		repeat(p, copy, from-1, to, reclimit);
  		break;
  	default:			/* "can't happen" */
  		SETERROR(REG_ASSERT);	/* just in case */
 @@ -1112,8 +1136,13 @@
  {
  	cset *cs, *ncs;
  
 +
 +	if (MEMSIZE(p) > MEMLIMIT)
 +		goto oomem;
 +
  	ncs = realloc(p->g->sets, (p->g->ncsets + 1) * sizeof(*ncs));
  	if (ncs == NULL) {
 +oomem:
  		SETERROR(REG_ESPACE);
  		return (NULL);
  	}
 @@ -1347,17 +1376,22 @@
  enlarge(struct parse *p, sopno size)
  {
  	sop *sp;
 +	sopno osize;
  
  	if (p->ssize >= size)
  		return 1;
 -
 +	osize = p->ssize;
 +	p->ssize = size;
 +	if (MEMSIZE(p) > MEMLIMIT)
 +		goto oomem;
  	sp = (sop *)realloc(p->strip, size*sizeof(sop));
  	if (sp == NULL) {
 +oomem:
 +		p->ssize = osize;
  		SETERROR(REG_ESPACE);
  		return 0;
  	}
  	p->strip = sp;
 -	p->ssize = size;
  	return 1;
  }
  
 Index: lib/libc/regex/regex2.h
 ===================================================================
 --- lib/libc/regex/regex2.h	(revision 265307)
 +++ lib/libc/regex/regex2.h	(working copy)
 @@ -73,7 +73,7 @@
   * immediately *preceding* "execution" of that operator.
   */
  typedef unsigned long sop;	/* strip operator */
 -typedef long sopno;
 +typedef size_t sopno;
  #define	OPRMASK	0xf8000000L
  #define	OPDMASK	0x07ffffffL
  #define	OPSHIFT	((unsigned)27)
 @@ -165,7 +165,7 @@
  	int magic;
  #		define	MAGIC2	((('R'^0200)<<8)|'E')
  	sop *strip;		/* malloced area for strip */
 -	int ncsets;		/* number of csets in use */
 +	size_t ncsets;		/* number of csets in use */
  	cset *sets;		/* -> cset [ncsets] */
  	int cflags;		/* copy of regcomp() cflags argument */
  	sopno nstates;		/* = number of sops */
 @@ -175,13 +175,13 @@
  #		define	USEBOL	01	/* used ^ */
  #		define	USEEOL	02	/* used $ */
  #		define	BAD	04	/* something wrong */
 -	int nbol;		/* number of ^ used */
 -	int neol;		/* number of $ used */
 +	size_t nbol;		/* number of ^ used */
 +	size_t neol;		/* number of $ used */
  	char *must;		/* match must contain this string */
  	int moffset;		/* latest point at which must may be located */
  	int *charjump;		/* Boyer-Moore char jump table */
  	int *matchjump;		/* Boyer-Moore match jump table */
 -	int mlen;		/* length of must */
 +	size_t mlen;		/* length of must */
  	size_t nsub;		/* copy of re_nsub */
  	int backrefs;		/* does it use back references? */
  	sopno nplus;		/* how deep does it nest +s? */
 
 --------------020708080805040202010709--
>Unformatted:
