diff options
author | Ingo Schwarze <schwarze@openbsd.org> | 2022-04-13 20:26:19 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@openbsd.org> | 2022-04-13 20:26:19 +0000 |
commit | 10729b3aa1181d3f40ecb3ba0f955f96d86eaee3 (patch) | |
tree | 23e0f420588718bcd80be303d809a557ee100167 /eqn.c | |
parent | 4062479d6729bdfb0de4954e7a413ca3282d17cd (diff) | |
download | mandoc-10729b3aa1181d3f40ecb3ba0f955f96d86eaee3.tar.gz mandoc-10729b3aa1181d3f40ecb3ba0f955f96d86eaee3.tar.zst mandoc-10729b3aa1181d3f40ecb3ba0f955f96d86eaee3.zip |
To prevent infinite recursion while expanding eqn(7) definitions,
we must not reset the recursion counter when moving beyond the end
of the *previous* expansion, but we may only do so when moving
beyond the rightmost position reached by *any* expansion in the
current equation. This matters because definitions can nest;
consider:
.EQ
define inner "content"
define outer "inner outer"
outer
.EN
This endless loop was found by tb@ using afl(1).
Incidentally, GNU eqn(1) also performs an infinite loop in this
situation and then crashes when memory runs out, but that's not an
excuse for nasty behaviour of mandoc(1).
While here, consistently print the expanded content even when the
expansion is finally truncated. While that is not likely to help
end-users, it may help authors of eqn(7) code to understand what's
going on. Besides, it sends a very clear signal that something is
amiss, which was easy to miss in the past unless people
enabled -W error or used -T lint.
Diffstat (limited to 'eqn.c')
-rw-r--r-- | eqn.c | 26 |
1 files changed, 15 insertions, 11 deletions
@@ -1,7 +1,8 @@ -/* $Id: eqn.c,v 1.84 2020/01/08 12:16:24 schwarze Exp $ */ +/* $Id: eqn.c,v 1.85 2022/04/13 20:26:19 schwarze Exp $ */ /* + * Copyright (c) 2014, 2015, 2017, 2018, 2020, 2022 + * Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -375,19 +376,17 @@ eqn_def_find(struct eqn_node *ep) static enum eqn_tok eqn_next(struct eqn_node *ep, enum parse_mode mode) { - static int last_len, lim; - struct eqn_def *def; size_t start; - int diff, i, quoted; + int diff, i, newlen, quoted; enum eqn_tok tok; /* * Reset the recursion counter after advancing - * beyond the end of the previous substitution. + * beyond the end of the rightmost substitution. */ - if (ep->end - ep->data >= last_len) - lim = 0; + if (ep->end - ep->data >= ep->sublen) + ep->subcnt = 0; ep->start = ep->end; quoted = mode == MODE_QUOTED; @@ -434,10 +433,10 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode) return EQN_TOK__MAX; if ((def = eqn_def_find(ep)) == NULL) break; - if (++lim > EQN_NEST_MAX) { + if (++ep->subcnt > EQN_NEST_MAX) { mandoc_msg(MANDOCERR_ROFFLOOP, ep->node->line, ep->node->pos, NULL); - return EQN_TOK_EOF; + break; } /* Replace a defined name with its string value. */ @@ -446,12 +445,15 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode) ep->sz += diff; ep->data = mandoc_realloc(ep->data, ep->sz + 1); ep->start = ep->data + start; + ep->sublen += diff; } if (diff) memmove(ep->start + def->valsz, ep->start + ep->toksz, strlen(ep->start + ep->toksz) + 1); memcpy(ep->start, def->val, def->valsz); - last_len = ep->start - ep->data + def->valsz; + newlen = ep->start - ep->data + def->valsz; + if (ep->sublen < newlen) + ep->sublen = newlen; } if (mode != MODE_TOK) return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX; @@ -678,6 +680,8 @@ eqn_parse(struct eqn_node *ep) return; ep->start = ep->end = ep->data; + ep->sublen = 0; + ep->subcnt = 0; next_tok: tok = eqn_next(ep, MODE_TOK); |