[njs] Fixed heap-buffer-overflow for RegExp.prototype[Symbol.replace].
Alexander Borisov
alexander.borisov at nginx.com
Tue Oct 13 12:45:23 UTC 2020
details: https://hg.nginx.org/njs/rev/a82f123409b7
branches:
changeset: 1539:a82f123409b7
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Tue Oct 13 15:44:33 2020 +0300
description:
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.
diffstat:
src/njs_regexp.c | 14 ++++++++------
src/test/njs_unit_test.c | 12 ++++++++++++
2 files changed, 20 insertions(+), 6 deletions(-)
diffs (72 lines):
diff -r 893fa730285c -r a82f123409b7 src/njs_regexp.c
--- a/src/njs_regexp.c Thu Oct 08 18:47:04 2020 +0300
+++ b/src/njs_regexp.c Tue Oct 13 15:44:33 2020 +0300
@@ -1343,14 +1343,14 @@ njs_regexp_prototype_symbol_replace(njs_
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_
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_
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_
}
ret = njs_string_get_substitution(vm, &matched, string, pos,
- arguments, ncaptures - 1, &groups,
+ arguments, ncaptures, &groups,
replace, &retval);
} else {
diff -r 893fa730285c -r a82f123409b7 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Thu Oct 08 18:47:04 2020 +0300
+++ b/src/test/njs_unit_test.c Tue Oct 13 15:44:33 2020 +0300
@@ -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("βγαβγ") },
More information about the nginx-devel
mailing list