>From d1e4a799076b7aa2ac3ba64c825c3fe3642983bc Mon Sep 17 00:00:00 2001 From: Szabolcs Nagy Date: Mon, 28 Sep 2026 12:34:05 +0000 Subject: [PATCH 07/25] regex: fix a|() recording an unselected empty match unselected empty subpattern recorded a match whenever the union matched because submatch bounds are determined as sub_start_tag = sub_end_tag = next_tag where next_tag is the tag immediately after the union that gets visited whenever it matches. each child branch of an union has a separate end tag already, so use those for submatch end bound instead of the next tag. these tags only get visited if the submatch is actually selected. a|($) vs "a" was (0,1)(1,1) now (0,1)(-1,-1) note: emptied submatch "regset" can change later tag allocation, but in case of a union it does not: parenthesis around it creates a submatch so regset is non-empty for followup nodes. this means the tnfa and exec time match selection does not change only submatch to tag mapping does. note2: empty () pattern is undefined in posix but a|($) seems valid. --- src/regex/regcomp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/regex/regcomp.c b/src/regex/regcomp.c index 04de3a3e..307a22b3 100644 --- a/src/regex/regcomp.c +++ b/src/regex/regcomp.c @@ -1577,6 +1577,7 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree, node->num_tags = ((tre_union_t *)node->obj)->left->num_tags + ((tre_union_t *)node->obj)->right->num_tags + added_tags; } + int *regset_right = regset; regset = tre_stack_pop_voidptr(stack); /* Add tags after both children, the left child gets a smaller @@ -1599,8 +1600,12 @@ tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree, if (status == REG_OK) status = tre_add_tag_right(mem, right, tag_right); tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE; + /* use the child end tags for submatches */ + tre_purge_regset(regset_right, tnfa, tag_right); + tre_purge_regset(regset, tnfa, tag_left); } num_tags += 2; + regset[0] = -1; } direction = TRE_TAG_MAXIMIZE; break; -- 2.52.0