[njs] Fixed heap-buffer-overflow in String.prototype.lastIndexOf().
Dmitry Volyntsev
xeioex at nginx.com
Fri May 28 14:42:58 UTC 2021
details: https://hg.nginx.org/njs/rev/f3eac82832aa
branches:
changeset: 1644:f3eac82832aa
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Fri May 28 14:00:04 2021 +0000
description:
Fixed heap-buffer-overflow in String.prototype.lastIndexOf().
Previously, the issue occurred when the searchValue is shorter
in character length than "this" string, but longer in byte length.
diffstat:
src/njs_string.c | 148 ++++++++++++++++++++--------------------------
src/test/njs_unit_test.c | 26 +++++++-
2 files changed, 89 insertions(+), 85 deletions(-)
diffs (225 lines):
diff -r 801e2dbc79c9 -r f3eac82832aa src/njs_string.c
--- a/src/njs_string.c Mon May 24 14:18:15 2021 +0000
+++ b/src/njs_string.c Fri May 28 14:00:04 2021 +0000
@@ -2230,114 +2230,94 @@ njs_string_prototype_last_index_of(njs_v
njs_uint_t nargs, njs_index_t unused)
{
double pos;
- ssize_t index, start, length, search_length;
+ int64_t index, start, length, search_length;
njs_int_t ret;
- njs_value_t *value, *search_string, lvalue;
+ njs_value_t *this, *search, search_lvalue;
const u_char *p, *end;
- njs_string_prop_t string, search;
-
- ret = njs_string_object_validate(vm, njs_arg(args, nargs, 0));
+ njs_string_prop_t string, s;
+
+ this = njs_argument(args, 0);
+
+ if (njs_slow_path(njs_is_null_or_undefined(this))) {
+ njs_type_error(vm, "cannot convert \"%s\"to object",
+ njs_type_string(this->type));
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_to_string(vm, this, this);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ search = njs_lvalue_arg(&search_lvalue, args, nargs, 1);
+ ret = njs_value_to_string(vm, search, search);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_value_to_number(vm, njs_arg(args, nargs, 2), &pos);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
- index = -1;
-
- length = njs_string_prop(&string, njs_argument(args, 0));
-
- search_string = njs_lvalue_arg(&lvalue, args, nargs, 1);
-
- if (njs_slow_path(!njs_is_string(search_string))) {
- ret = njs_value_to_string(vm, search_string, search_string);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
- }
-
- search_length = njs_string_prop(&search, search_string);
-
- if (length < search_length) {
- goto done;
- }
-
- value = njs_arg(args, nargs, 2);
-
- if (njs_slow_path(!njs_is_number(value))) {
- ret = njs_value_to_number(vm, value, &pos);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
+ if (!isnan(pos)) {
+ start = njs_number_to_integer(pos);
} else {
- pos = njs_number(value);
+ start = INT64_MAX;
}
- if (isnan(pos)) {
- index = NJS_STRING_MAX_LENGTH;
-
- } else {
- index = njs_number_to_integer(pos);
-
- if (index < 0) {
- index = 0;
- }
+ length = njs_string_prop(&string, this);
+
+ start = njs_min(njs_max(start, 0), length);
+
+ search_length = njs_string_prop(&s, search);
+
+ index = length - search_length;
+
+ if (index > start) {
+ index = start;
}
- if (search_length == 0) {
- index = njs_min(index, length);
- goto done;
- }
-
- if (index >= length) {
- index = length - 1;
- }
+ end = string.start + string.size;
if (string.size == (size_t) length) {
/* Byte or ASCII string. */
- start = length - search.size;
-
- if (index > start) {
- index = start;
+ p = &string.start[index];
+
+ if (p > end - s.size) {
+ p = end - s.size;
+ }
+
+ for (; p >= string.start; p--) {
+ if (memcmp(p, s.start, s.size) == 0) {
+ index = p - string.start;
+ goto done;
+ }
}
- p = string.start + index;
-
- do {
- if (memcmp(p, search.start, search.size) == 0) {
+ index = -1;
+
+ } else {
+ /* UTF-8 string. */
+
+ if (index < 0 || index == length) {
+ index = (search_length == 0) ? index : -1;
+ goto done;
+ }
+
+ p = njs_string_offset(string.start, end, index);
+
+ for (; p >= string.start; p = njs_utf8_prev(p)) {
+ if ((p + s.size) <= end && memcmp(p, s.start, s.size) == 0) {
goto done;
}
index--;
- p--;
-
- } while (p >= string.start);
-
- } else {
- /* UTF-8 string. */
-
- end = string.start + string.size;
- p = njs_string_offset(string.start, end, index);
- end -= search.size;
-
- while (p > end) {
- index--;
- p = njs_utf8_prev(p);
}
- for ( ;; ) {
- if (memcmp(p, search.start, search.size) == 0) {
- goto done;
- }
-
- index--;
-
- if (p <= string.start) {
- break;
- }
-
- p = njs_utf8_prev(p);
- }
+ index = -1;
}
done:
diff -r 801e2dbc79c9 -r f3eac82832aa src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Mon May 24 14:18:15 2021 +0000
+++ b/src/test/njs_unit_test.c Fri May 28 14:00:04 2021 +0000
@@ -8008,7 +8008,28 @@ static njs_unit_test_t njs_test[] =
{ njs_str("'a a'.toUTF8().indexOf('a', 1)"),
njs_str("2") },
- { njs_str("'abc'.lastIndexOf('abcdef')"),
+ { njs_str("'aaa'.lastIndexOf()"),
+ njs_str("-1") },
+
+ { njs_str("'aaa'.lastIndexOf('')"),
+ njs_str("3") },
+
+ { njs_str("'aaa'.lastIndexOf('a')"),
+ njs_str("2") },
+
+ { njs_str("'aaa'.lastIndexOf('aa')"),
+ njs_str("1") },
+
+ { njs_str("'aaa'.lastIndexOf('aaa')"),
+ njs_str("0") },
+
+ { njs_str("'aaa'.lastIndexOf('aaaa')"),
+ njs_str("-1") },
+
+ { njs_str("'a'.repeat(16).lastIndexOf(String.fromCodePoint(65533).repeat(15))"),
+ njs_str("-1") },
+
+ { njs_str("('α'+'a'.repeat(15)).lastIndexOf(String.fromCodePoint(65533).repeat(15))"),
njs_str("-1") },
{ njs_str("'abc abc abc abc'.lastIndexOf('abc')"),
@@ -8077,6 +8098,9 @@ static njs_unit_test_t njs_test[] =
{ njs_str("'β'.repeat(32).lastIndexOf('β')"),
njs_str("31") },
+ { njs_str("'β'.repeat(32).lastIndexOf('β'.repeat(32))"),
+ njs_str("0") },
+
{ njs_str("'β'.repeat(32).lastIndexOf``"),
njs_str("32") },
More information about the nginx-devel
mailing list