rp->tmp = rp; /* mark end */
deltraverse(nfa, lp, lp);
+ if (NISERR())
+ return; /* asserts might not hold after failure */
assert(lp->nouts == 0 && rp->nins == 0); /* did the job */
assert(lp->no != FREESTATE && rp->no != FREESTATE); /* no more */
struct arc *a;
struct state *to;
+ /* Since this is recursive, it could be driven to stack overflow */
+ if (STACK_TOO_DEEP(nfa->v->re))
+ {
+ NERR(REG_ETOOBIG);
+ return;
+ }
+
if (s->nouts == 0)
return; /* nothing to do */
if (s->tmp != NULL)
{
to = a->to;
deltraverse(nfa, leftend, to);
+ if (NISERR())
+ return; /* asserts might not hold after failure */
assert(to->nouts == 0 || to->tmp != NULL);
freearc(nfa, a);
if (to->nins == 0 && to->tmp == NULL)
{
struct arc *a;
+ /* Since this is recursive, it could be driven to stack overflow */
+ if (STACK_TOO_DEEP(nfa->v->re))
+ {
+ NERR(REG_ETOOBIG);
+ return;
+ }
+
if (s->tmp != NULL)
return; /* already done */
{
struct arc *a;
+ /* Since this is recursive, it could be driven to stack overflow */
+ if (STACK_TOO_DEEP(nfa->v->re))
+ {
+ NERR(REG_ETOOBIG);
+ return;
+ }
+
if (s->tmp == NULL)
return;
s->tmp = NULL;
*/
for (s = nfa->states; s != NULL && !NISERR(); s = s->next)
{
- for (s2 = emptyreachable(s, s); s2 != s && !NISERR(); s2 = nexts)
+ for (s2 = emptyreachable(nfa, s, s); s2 != s && !NISERR(); s2 = nexts)
{
/*
* If s2 is doomed, we decide that (1) we will always push arcs
*
* The maximum recursion depth here is equal to the length of the longest
* loop-free chain of EMPTY arcs, which is surely no more than the size of
- * the NFA, and in practice will be a lot less than that.
+ * the NFA ... but that could still be enough to cause trouble.
*/
static struct state *
-emptyreachable(struct state * s, struct state * lastfound)
+emptyreachable(struct nfa * nfa,
+ struct state * s,
+ struct state * lastfound)
{
struct arc *a;
+ /* Since this is recursive, it could be driven to stack overflow */
+ if (STACK_TOO_DEEP(nfa->v->re))
+ {
+ NERR(REG_ETOOBIG);
+ return lastfound;
+ }
+
s->tmp = lastfound;
lastfound = s;
for (a = s->outs; a != NULL; a = a->outchain)
{
if (a->type == EMPTY && a->to->tmp == NULL)
- lastfound = emptyreachable(a->to, lastfound);
+ lastfound = emptyreachable(nfa, a->to, lastfound);
}
return lastfound;
}
struct state *nexts;
int n;
+ if (NISERR())
+ return;
+
/* clear out unreachable or dead-end states */
/* use pre to mark reachable, then post to mark can-reach-post */
markreachable(nfa, nfa->pre, (struct state *) NULL, nfa->pre);
markcanreach(nfa, nfa->post, nfa->pre, nfa->post);
- for (s = nfa->states; s != NULL; s = nexts)
+ for (s = nfa->states; s != NULL && !NISERR(); s = nexts)
{
nexts = s->next;
if (s->tmp != nfa->post && !s->flag)
dropstate(nfa, s);
}
- assert(nfa->post->nins == 0 || nfa->post->tmp == nfa->post);
+ assert(NISERR() || nfa->post->nins == 0 || nfa->post->tmp == nfa->post);
cleartraverse(nfa, nfa->pre);
- assert(nfa->post->nins == 0 || nfa->post->tmp == NULL);
+ assert(NISERR() || nfa->post->nins == 0 || nfa->post->tmp == NULL);
/* the nins==0 (final unreachable) case will be caught later */
/* renumber surviving states */
{
struct arc *a;
+ /* Since this is recursive, it could be driven to stack overflow */
+ if (STACK_TOO_DEEP(nfa->v->re))
+ {
+ NERR(REG_ETOOBIG);
+ return;
+ }
+
if (s->tmp != okay)
return;
s->tmp = mark;
{
struct arc *a;
+ /* Since this is recursive, it could be driven to stack overflow */
+ if (STACK_TOO_DEEP(nfa->v->re))
+ {
+ NERR(REG_ETOOBIG);
+ return;
+ }
+
if (s->tmp != okay)
return;
s->tmp = mark;
struct arc *a;
struct arc *aa;
+ if (NISERR())
+ return 0;
+
if (nfa->pre->outs == NULL)
return REG_UIMPOSSIBLE;
for (a = nfa->pre->outs; a != NULL; a = a->outchain)
#include "regex/regguts.h"
-#include "miscadmin.h" /* needed by rcancelrequested() */
+#include "miscadmin.h" /* needed by rcancelrequested/rstacktoodeep */
/*
* forward declarations, up here so forward datatypes etc. are defined early
static void freelacons(struct subre *, int);
static void rfree(regex_t *);
static int rcancelrequested(void);
+static int rstacktoodeep(void);
#ifdef REG_DEBUG
static void dump(regex_t *, FILE *);
#define COMPATIBLE 3 /* compatible but not satisfied yet */
static int combine(struct arc *, struct arc *);
static void fixempties(struct nfa *, FILE *);
-static struct state *emptyreachable(struct state *, struct state *);
+static struct state *emptyreachable(struct nfa *, struct state *, struct state *);
static void replaceempty(struct nfa *, struct state *, struct state *);
static void cleanup(struct nfa *);
static void markreachable(struct nfa *, struct state *, struct state *, struct state *);
/* static function list */
static const struct fns functions = {
rfree, /* regfree insides */
- rcancelrequested /* check for cancel request */
+ rcancelrequested, /* check for cancel request */
+ rstacktoodeep /* check for stack getting dangerously deep */
};
{
struct subre *ret = v->treefree;
+ /*
+ * Checking for stack overflow here is sufficient to protect parse() and
+ * its recursive subroutines.
+ */
+ if (STACK_TOO_DEEP(v->re))
+ {
+ ERR(REG_ETOOBIG);
+ return NULL;
+ }
+
if (ret != NULL)
v->treefree = ret->left;
else
return InterruptPending && (QueryCancelPending || ProcDiePending);
}
+/*
+ * rstacktoodeep - check for stack getting dangerously deep
+ *
+ * Return nonzero to fail the operation with error code REG_ETOOBIG,
+ * zero to keep going
+ *
+ * The current implementation is Postgres-specific. If we ever get around
+ * to splitting the regex code out as a standalone library, there will need
+ * to be some API to let applications define a callback function for this.
+ */
+static int
+rstacktoodeep(void)
+{
+ return stack_is_too_deep();
+}
+
#ifdef REG_DEBUG
/*
struct smalldfa sd;
chr *end;
+ /* Since this is recursive, it could be driven to stack overflow */
+ if (STACK_TOO_DEEP(v->re))
+ {
+ ERR(REG_ETOOBIG);
+ return 0;
+ }
+
n = co - pcnfa->ncolors;
assert(n < v->g->nlacons && v->g->lacons != NULL);
FDEBUG(("=== testing lacon %d\n", n));
/* handy place to check for operation cancel */
if (CANCEL_REQUESTED(v->re))
return REG_CANCEL;
+ /* ... and stack overrun */
+ if (STACK_TOO_DEEP(v->re))
+ return REG_ETOOBIG;
switch (t->op)
{
}
/*
- * check_stack_depth: check for excessively deep recursion
+ * check_stack_depth/stack_is_too_deep: check for excessively deep recursion
*
* This should be called someplace in any recursive routine that might possibly
* recurse deep enough to overflow the stack. Most Unixen treat stack
* overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
* before hitting the hardware limit.
+ *
+ * check_stack_depth() just throws an error summarily. stack_is_too_deep()
+ * can be used by code that wants to handle the error condition itself.
*/
void
check_stack_depth(void)
+{
+ if (stack_is_too_deep())
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
+ errmsg("stack depth limit exceeded"),
+ errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
+ "after ensuring the platform's stack depth limit is adequate.",
+ max_stack_depth)));
+ }
+}
+
+bool
+stack_is_too_deep(void)
{
char stack_top_loc;
long stack_depth;
*/
if (stack_depth > max_stack_depth_bytes &&
stack_base_ptr != NULL)
- {
- ereport(ERROR,
- (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
- errmsg("stack depth limit exceeded"),
- errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
- "after ensuring the platform's stack depth limit is adequate.",
- max_stack_depth)));
- }
+ return true;
/*
* On IA64 there is a separate "register" stack that requires its own
if (stack_depth > max_stack_depth_bytes &&
register_stack_base_ptr != NULL)
- {
- ereport(ERROR,
- (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
- errmsg("stack depth limit exceeded"),
- errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
- "after ensuring the platform's stack depth limit is adequate.",
- max_stack_depth)));
- }
+ return true;
#endif /* IA64 */
+
+ return false;
}
/* GUC check hook for max_stack_depth */
extern pg_stack_base_t set_stack_base(void);
extern void restore_stack_base(pg_stack_base_t base);
extern void check_stack_depth(void);
+extern bool stack_is_too_deep(void);
/* in tcop/utility.c */
extern void PreventCommandIfReadOnly(const char *cmdname);
{
void FUNCPTR(free, (regex_t *));
int FUNCPTR(cancel_requested, (void));
+ int FUNCPTR(stack_too_deep, (void));
};
#define CANCEL_REQUESTED(re) \
((*((struct fns *) (re)->re_fns)->cancel_requested) ())
+#define STACK_TOO_DEEP(re) \
+ ((*((struct fns *) (re)->re_fns)->stack_too_deep) ())
+
/*
* the insides of a regex_t, hidden behind a void *