diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2018-04-04 23:04:16 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2018-04-04 23:04:16 -0700 |
commit | a9af2631068edfcb26b1823cf63b078ddf75dd79 (patch) | |
tree | 2f70b2c0e370d161e05e5a04dd053e9e61245be0 /regex.c | |
parent | 0cb57f957f6ea07a8e33173d39716716db455d30 (diff) | |
download | txr-a9af2631068edfcb26b1823cf63b078ddf75dd79.tar.gz txr-a9af2631068edfcb26b1823cf63b078ddf75dd79.tar.bz2 txr-a9af2631068edfcb26b1823cf63b078ddf75dd79.zip |
regex: fix double free corruption bug.
Unfortunately, the nfa_state_free function doesn't check the
static flag on a character set and just calls chr_set_destroy.
So when one of the static character sets is planted into the
NFA graph, when that graph is garbage-collected, it blows away
the static character set. Then when that happens twice for the
same set, boom!
We make an alteration to make the destruction more defensive.
Callers of char_set_destroy are no longer saddled with the
responsibility of honoring the static flag buried in the
object. Instead, that function itself check the static flag.
An argument is provided to force the deletion in spite of the
static flag; that is needed for the global cleanup of the
static states. (Only occurs if txr is run with --free-all
and cleanly exited.)
* regex.c (char_set_destroy): Take extra argument, force.
If the set is marked static, then do nothing, unless
force is nonzero.
(char_set_cobj_destroy): Don't check the static flag, just
call char_set_destroy, force zero.
(nfa_state_free): Add force zero argument to char_set_destroy
call. The double free bug is thereby fixed here; static sets
are protected.
(regex_free_all): Force all the char_set_destroy calls here.
Diffstat (limited to 'regex.c')
-rw-r--r-- | regex.c | 24 |
1 files changed, 13 insertions, 11 deletions
@@ -547,11 +547,14 @@ static char_set_t *char_set_create(chset_type_t type, wchar_t base, unsigned st) return cs; } -static void char_set_destroy(char_set_t *set) +static void char_set_destroy(char_set_t *set, int force) { if (!set) return; + if (set->any.stat && !force) + return; + switch (set->any.type) { case CHSET_DISPLACED: case CHSET_SMALL: @@ -799,8 +802,7 @@ static void init_special_char_sets(void) static void char_set_cobj_destroy(val chset) { char_set_t *set = coerce(char_set_t *, chset->co.handle); - if (!set->any.stat) - char_set_destroy(set); + char_set_destroy(set, 0); chset->co.handle = 0; } @@ -852,7 +854,7 @@ static nfa_state_t *nfa_state_wild(nfa_state_t *t) static void nfa_state_free(nfa_state_t *st) { if (st->a.kind == nfa_set) - char_set_destroy(st->s.set); + char_set_destroy(st->s.set, 0); free(st); } @@ -3303,11 +3305,11 @@ void regex_init(void) void regex_free_all(void) { - char_set_destroy(space_cs); - char_set_destroy(digit_cs); - char_set_destroy(word_cs); - char_set_destroy(cspace_cs); - char_set_destroy(cdigit_cs); - char_set_destroy(cword_cs); - char_set_destroy(wide_cs); + char_set_destroy(space_cs, 1); + char_set_destroy(digit_cs, 1); + char_set_destroy(word_cs, 1); + char_set_destroy(cspace_cs, 1); + char_set_destroy(cdigit_cs, 1); + char_set_destroy(cword_cs, 1); + char_set_destroy(wide_cs, 1); } |