From: Alexander Borisov Date: Tue, 13 Oct 2020 12:44:33 +0000 (+0300) Subject: Fixed heap-buffer-overflow for RegExp.prototype[Symbol.replace]. X-Git-Url: http://www.kaiwu.me/postgresql/commit/static/gitweb.js?a=commitdiff_plain;h=495c0c1bbf7c952a74ef180d1ff8da8e4983c5f7;p=njs.git 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. --- 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("βγαβγ") },