Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions smoke.c
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,51 @@ void test_depth() {
r = te_interp(expr, &err);
lok(ok ? (err == 0) : (err != 0 && r != r));
free(expr);

/* 1+1+1+...+1: a flat run of same-precedence operators builds a
* parse tree exactly as deep as nested parens do, even though
* parsing it never recurses through base(). It must be bounded
* the same way instead of overflowing the stack in te_eval(),
* te_free() or the constant-folding pass in te_compile(). */
expr = malloc((size_t)depth * 2 + 2);
if (!expr) { lok(0); continue; }
expr[0] = '1';
for (j = 0; j < depth; ++j) {
expr[1 + j * 2] = '+';
expr[2 + j * 2] = '1';
}
expr[depth * 2 + 1] = '\0';

r = te_interp(expr, &err);
lok(ok ? (err == 0 && r == depth + 1) : (err != 0 && r != r));
free(expr);
}

{
/* Two independent, deeply nested arguments to the same call must
* not have their depths added together: each is only as deep as
* it looks on its own, so atan2() of two 400-deep operands is
* still well within TE_MAX_DEPTH and must succeed. */
const int depth = 400;
char *nested = malloc((size_t)depth * 2 + 2);
lok(nested != NULL);
if (nested) {
memset(nested, '(', depth);
nested[depth] = '1';
memset(nested + depth + 1, ')', depth);
nested[depth * 2 + 1] = '\0';

char *expr = malloc(strlen(nested) * 2 + 16);
lok(expr != NULL);
if (expr) {
sprintf(expr, "atan2(%s,%s)", nested, nested);
int err;
double r = te_interp(expr, &err);
lok(err == 0 && fabs(r - atan2(1.0, 1.0)) < 1e-9);
free(expr);
}
free(nested);
}
}
}

Expand Down
33 changes: 33 additions & 0 deletions tinyexpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ typedef struct state {
#define TE_MAX_DEPTH 512
#endif

/* te_eval(), te_free() and optimize() all walk the parsed expression
recursively, one call frame per level of the tree. base() already
refuses to nest more than TE_MAX_DEPTH deep so parsing itself can't
blow the stack, but a long run of same-precedence operators such as
"1+1+1+...+1" builds an equally deep tree without ever recursing
through base(), so it slipped past that guard. TE_CHAIN_GUARD applies
the same limit inside the iterative operator-chain parsers below. */
#define TE_CHAIN_GUARD(...) if (++s->depth >= TE_MAX_DEPTH) { s->type = TOK_ERROR; __VA_ARGS__; return NULL; }

static te_expr *new_expr(const int type, const te_expr *parameters[]) {
const int arity = ARITY(type);
const int psize = sizeof(void*) * arity;
Expand Down Expand Up @@ -529,7 +538,13 @@ static te_expr *base_impl(state *s) {
s->type = TOK_ERROR;
} else {
int i;
const int arg_depth = s->depth;
for(i = 0; i < arity; i++) {
/* Each argument is evaluated independently of its
siblings, so it should be judged against the depth
this call started at rather than accumulating
across arguments. */
s->depth = arg_depth;
next_token(s);
ret->parameters[i] = expr(s);
CHECK_NULL(ret->parameters[i], te_free(ret));
Expand Down Expand Up @@ -655,6 +670,8 @@ static te_expr *factor(state *s) {
void **slot = NULL;

while (s->type == TOK_INFIX && (s->function == pow)) {
TE_CHAIN_GUARD(te_free(ret));

te_fun2 t = (te_fun2)s->function;
next_token(s);

Expand Down Expand Up @@ -696,6 +713,8 @@ static te_expr *factor(state *s) {
CHECK_NULL(ret);

while (s->type == TOK_INFIX && (s->function == pow)) {
TE_CHAIN_GUARD(te_free(ret));

te_fun2 t = (te_fun2)s->function;
next_token(s);
te_expr *p = power(s);
Expand All @@ -720,6 +739,8 @@ static te_expr *term(state *s) {
CHECK_NULL(ret);

while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) {
TE_CHAIN_GUARD(te_free(ret));

te_fun2 t = (te_fun2)s->function;
next_token(s);
te_expr *f = factor(s);
Expand All @@ -742,6 +763,8 @@ static te_expr *sum_expr(state *s) {
CHECK_NULL(ret);

while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) {
TE_CHAIN_GUARD(te_free(ret));

te_fun2 t = (te_fun2)s->function;
next_token(s);
te_expr *te = term(s);
Expand All @@ -765,6 +788,8 @@ static te_expr *rel_expr(state *s) {

while (s->type == TOK_INFIX && (s->function == greater || s->function == greater_eq ||
s->function == lower || s->function == lower_eq)) {
TE_CHAIN_GUARD(te_free(ret));

te_fun2 t = (te_fun2)s->function;
next_token(s);
te_expr *e = sum_expr(s);
Expand All @@ -787,6 +812,8 @@ static te_expr *eq_expr(state *s) {
CHECK_NULL(ret);

while (s->type == TOK_INFIX && (s->function == equal || s->function == not_equal)) {
TE_CHAIN_GUARD(te_free(ret));

te_fun2 t = (te_fun2)s->function;
next_token(s);
te_expr *e = rel_expr(s);
Expand All @@ -809,6 +836,8 @@ static te_expr *and_expr(state *s) {
CHECK_NULL(ret);

while (s->type == TOK_INFIX && s->function == logical_and) {
TE_CHAIN_GUARD(te_free(ret));

next_token(s);
te_expr *e = eq_expr(s);
CHECK_NULL(e, te_free(ret));
Expand All @@ -830,6 +859,8 @@ static te_expr *expr(state *s) {
CHECK_NULL(ret);

while (s->type == TOK_INFIX && s->function == logical_or) {
TE_CHAIN_GUARD(te_free(ret));

next_token(s);
te_expr *e = and_expr(s);
CHECK_NULL(e, te_free(ret));
Expand All @@ -851,6 +882,8 @@ static te_expr *list(state *s) {
CHECK_NULL(ret);

while (s->type == TOK_SEP) {
TE_CHAIN_GUARD(te_free(ret));

next_token(s);
te_expr *e = expr(s);
CHECK_NULL(e, te_free(ret));
Expand Down