[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