Previously, PCRE name-table capture indexes were decoded through plain char.
On signed-char targets, bytes with the high bit set were sign-extended and
produced a wild or incorrect capture index used to read a match array.
The fix is to decode unsigned bytes through a checked output parameter and to
validate every named capture index against the compiled capture count.
The issue was introduced in
9496944a (0.3.2).
This closes #1088 issue on GitHub.
njs_int_t
-njs_regex_named_captures(njs_regex_t *regex, njs_str_t *name, int n)
+njs_regex_named_capture(njs_regex_t *regex, njs_str_t *name, int n,
+ uint32_t *capture)
{
- char *entry;
+ u_char *entry;
if (name == NULL) {
return regex->nentries;
entry = regex->entries + regex->entry_size * n;
- name->start = (u_char *) entry + 2;
+ name->start = entry + 2;
name->length = njs_strlen(name->start);
- return (entry[0] << 8) + entry[1];
+ *capture = (entry[0] << 8) + entry[1];
+
+ return NJS_OK;
}
#endif
-
int backrefmax;
int nentries;
int entry_size;
- char *entries;
+ u_char *entries;
} njs_regex_t;
size_t len, njs_regex_flags_t flags, njs_regex_compile_ctx_t *ctx,
njs_trace_t *trace);
NJS_EXPORT njs_bool_t njs_regex_is_valid(njs_regex_t *regex);
-NJS_EXPORT njs_int_t njs_regex_named_captures(njs_regex_t *regex,
- njs_str_t *name, int n);
+NJS_EXPORT njs_int_t njs_regex_named_capture(njs_regex_t *regex,
+ njs_str_t *name, int n, uint32_t *capture);
NJS_EXPORT njs_regex_match_data_t *njs_regex_match_data(njs_regex_t *regex,
njs_regex_generic_ctx_t *ctx);
NJS_EXPORT void njs_regex_match_data_free(njs_regex_match_data_t *match_data,
goto fail;
}
- pattern->ngroups = njs_regex_named_captures(regex, NULL, 0);
+ pattern->ngroups = njs_regex_named_capture(regex, NULL, 0, NULL);
if (pattern->ngroups != 0) {
size = sizeof(njs_regexp_group_t) * pattern->ngroups;
do {
group = &pattern->groups[n];
- group->capture = njs_regex_named_captures(regex, &group->name, n);
+ ret = njs_regex_named_capture(regex, &group->name, n,
+ &group->capture);
+ if (njs_slow_path(ret != NJS_OK
+ || group->capture >= pattern->ncaptures))
+ {
+ njs_internal_error(vm, "invalid named capture index");
+ goto fail;
+ }
+
group->hash = njs_djb_hash(group->name.start, group->name.length);
n++;
{ njs_str("'ABC'.replace(/(?<b>B)/, '|$<BB$$|>@')"),
njs_str("A|@C") },
+ { njs_str("var s = '(?:' + '()'.repeat(127) + '){0}(?<x>a)';"
+ "new RegExp(s).exec('a').groups.x"),
+ njs_str("a") },
+
+ { njs_str("var s = '(?:' + '()'.repeat(383) + '){0}(?<x>a)';"
+ "new RegExp(s).exec('a').groups.x"),
+ njs_str("a") },
+
{ njs_str("'ABCB'.replaceAll(/(?<b>B)/g, '|$<BB$$|>@')"),
njs_str("A|@C|@") },