From 495c0c1bbf7c952a74ef180d1ff8da8e4983c5f7 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Tue, 13 Oct 2020 15:44:33 +0300 Subject: [PATCH] Fixed heap-buffer-overflow for RegExp.prototype[Symbol.replace]. Previously, RegExp.prototype[Symbol.replace] might overrun the boundaries of the result of the custom "exec" method for a RegExp argument. The issue occurred when the result object had zero length. The length is used to create an array and the zero index was always written without respect for the length resulting is heap-buffer-overflow. The issue was introduced in 1c729f765cfb. --- src/njs_regexp.c | 14 ++++++++------ src/test/njs_unit_test.c | 12 ++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/njs_regexp.c b/src/njs_regexp.c index ed9a1145..81dd2e5c 100644 --- a/src/njs_regexp.c +++ b/src/njs_regexp.c @@ -1343,14 +1343,14 @@ njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, pos = njs_max(njs_min(pos, (int64_t) s.size), 0); - if (njs_fast_path(njs_is_fast_array(r))) { + if (njs_fast_path(njs_is_fast_array(r) && njs_array_len(r) != 0)) { array = njs_array(r); arguments = array->start; arguments[0] = matched; - ncaptures = array->length; + ncaptures = njs_max((int64_t) array->length - 1, 0); - for (n = 1; n < ncaptures; n++) { + for (n = 1; n <= ncaptures; n++) { if (njs_is_undefined(&arguments[n])) { continue; } @@ -1367,7 +1367,9 @@ njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, goto exception; } - array = njs_array_alloc(vm, 0, ncaptures, 0); + ncaptures = njs_max(ncaptures - 1, 0); + + array = njs_array_alloc(vm, 0, ncaptures + 1, 0); if (njs_slow_path(array == NULL)) { goto exception; } @@ -1375,7 +1377,7 @@ njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, arguments = array->start; arguments[0] = matched; - for (n = 1; n < ncaptures; n++) { + for (n = 1; n <= ncaptures; n++) { ret = njs_value_property_i64(vm, r, n, &arguments[n]); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; @@ -1406,7 +1408,7 @@ njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, } ret = njs_string_get_substitution(vm, &matched, string, pos, - arguments, ncaptures - 1, &groups, + arguments, ncaptures, &groups, replace, &retval); } else { diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index f79e2b9e..435fb2ad 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -8284,6 +8284,18 @@ static njs_unit_test_t njs_test[] = { njs_str("/b(c)(z)?(.)/[Symbol.replace]('abcde', '[$01$02$03$04$00]')"), njs_str("a[cd$04$00]e") }, + { njs_str("var r = /./; r.exec = () => {return {}};" + "r[Symbol.replace]('ABCD', 'b')"), + njs_str("b") }, + + { njs_str("var r = /./; r.exec = () => {return {}};" + "r[Symbol.replace]('ABCD', (m,p,o) => `${m}|${p}|${o}`)"), + njs_str("undefined|0|ABCD") }, + + { njs_str("var r = /./; r.exec = () => Buffer.from([]).toJSON().data;" + "r[Symbol.replace]('ABCD', 'b')"), + njs_str("b") }, + { njs_str("'α'.replace(/(h*)/g, '$1βγ')"), njs_str("βγαβγ") }, -- 2.47.3