[njs] Fixed String.toLowerCase() and String.toUpperCase().

Valentin Bartenev vbart at nginx.com
Sat Jul 27 14:35:45 UTC 2019


details:   https://hg.nginx.org/njs/rev/cba3bd29f263
branches:  
changeset: 1076:cba3bd29f263
user:      Valentin Bartenev <vbart at nginx.com>
date:      Sat Jul 27 17:03:02 2019 +0300
description:
Fixed String.toLowerCase() and String.toUpperCase().

Previously these functions didn't took into account that the size of
a string may change during case transformation resulting in buffer
underflow or overflow.

diffstat:

 njs/njs_string.c         |  88 +++++++++++++++++++++++++++++++++--------------
 njs/test/njs_unit_test.c |  11 ++++-
 2 files changed, 71 insertions(+), 28 deletions(-)

diffs (176 lines):

diff -r 0b82f1c9268c -r cba3bd29f263 njs/njs_string.c
--- a/njs/njs_string.c	Sat Jul 27 16:12:26 2019 +0300
+++ b/njs/njs_string.c	Sat Jul 27 17:03:02 2019 +0300
@@ -2171,24 +2171,24 @@ njs_string_prototype_to_lower_case(njs_v
     nxt_uint_t nargs, njs_index_t unused)
 {
     size_t             size, length;
-    u_char             *p, *start;
+    u_char             *p;
+    uint32_t           code;
     const u_char       *s, *end;
     njs_string_prop_t  string;
 
     (void) njs_string_prop(&string, &args[0]);
 
-    start = njs_string_alloc(vm, &vm->retval, string.size, string.length);
-    if (nxt_slow_path(start == NULL)) {
-        return NXT_ERROR;
-    }
-
-    p = start;
-    s = string.start;
-    size = string.size;
-
-    if (string.length == 0 || string.length == size) {
+    if (string.length == 0 || string.length == string.size) {
         /* Byte or ASCII string. */
 
+        p = njs_string_alloc(vm, &vm->retval, string.size, string.length);
+        if (nxt_slow_path(p == NULL)) {
+            return NXT_ERROR;
+        }
+
+        s = string.start;
+        size = string.size;
+
         while (size != 0) {
             *p++ = nxt_lower_case(*s++);
             size--;
@@ -2196,11 +2196,29 @@ njs_string_prototype_to_lower_case(njs_v
 
     } else {
         /* UTF-8 string. */
-        end = s + size;
+        s = string.start;
+        end = s + string.size;
+        length = string.length;
+
+        size = 0;
+
+        while (length != 0) {
+            code = nxt_utf8_lower_case(&s, end);
+            size += nxt_utf8_size(code);
+            length--;
+        }
+
+        p = njs_string_alloc(vm, &vm->retval, size, string.length);
+        if (nxt_slow_path(p == NULL)) {
+            return NXT_ERROR;
+        }
+
+        s = string.start;
         length = string.length;
 
         while (length != 0) {
-            p = nxt_utf8_encode(p, nxt_utf8_lower_case(&s, end));
+            code = nxt_utf8_lower_case(&s, end);
+            p = nxt_utf8_encode(p, code);
             length--;
         }
     }
@@ -2220,24 +2238,24 @@ njs_string_prototype_to_upper_case(njs_v
     nxt_uint_t nargs, njs_index_t unused)
 {
     size_t             size, length;
-    u_char             *p, *start;
+    u_char             *p;
+    uint32_t           code;
     const u_char       *s, *end;
     njs_string_prop_t  string;
 
     (void) njs_string_prop(&string, &args[0]);
 
-    start = njs_string_alloc(vm, &vm->retval, string.size, string.length);
-    if (nxt_slow_path(start == NULL)) {
-        return NXT_ERROR;
-    }
-
-    p = start;
-    s = string.start;
-    size = string.size;
-
-    if (string.length == 0 || string.length == size) {
+    if (string.length == 0 || string.length == string.size) {
         /* Byte or ASCII string. */
 
+        p = njs_string_alloc(vm, &vm->retval, string.size, string.length);
+        if (nxt_slow_path(p == NULL)) {
+            return NXT_ERROR;
+        }
+
+        s = string.start;
+        size = string.size;
+
         while (size != 0) {
             *p++ = nxt_upper_case(*s++);
             size--;
@@ -2245,11 +2263,29 @@ njs_string_prototype_to_upper_case(njs_v
 
     } else {
         /* UTF-8 string. */
-        end = s + size;
+        s = string.start;
+        end = s + string.size;
+        length = string.length;
+
+        size = 0;
+
+        while (length != 0) {
+            code = nxt_utf8_upper_case(&s, end);
+            size += nxt_utf8_size(code);
+            length--;
+        }
+
+        p = njs_string_alloc(vm, &vm->retval, size, string.length);
+        if (nxt_slow_path(p == NULL)) {
+            return NXT_ERROR;
+        }
+
+        s = string.start;
         length = string.length;
 
         while (length != 0) {
-            p = nxt_utf8_encode(p, nxt_utf8_upper_case(&s, end));
+            code = nxt_utf8_upper_case(&s, end);
+            p = nxt_utf8_encode(p, code);
             length--;
         }
     }
diff -r 0b82f1c9268c -r cba3bd29f263 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Sat Jul 27 16:12:26 2019 +0300
+++ b/njs/test/njs_unit_test.c	Sat Jul 27 17:03:02 2019 +0300
@@ -5561,16 +5561,24 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'АБВ'.toLowerCase()"),
       nxt_string("абв") },
 
+    { nxt_string("'Ȿ'.repeat(256).toLowerCase() === 'ȿ'.repeat(256)"),
+      nxt_string("true") },
+
     { nxt_string("'abc'.toUpperCase()"),
       nxt_string("ABC") },
 
     { nxt_string("'αβγ'.toUpperCase()"),
       nxt_string("ΑΒΓ") },
 
+    { nxt_string("'ȿ'.repeat(256).toUpperCase() === 'Ȿ'.repeat(256)"),
+      nxt_string("true") },
+
     { nxt_string("'\x00абвгдеёжз'.toUpperCase().length"),
       nxt_string("10") },
 
-#if 0 /* FIXME */
+    { nxt_string("['ȿ', 'Ȿ', 'ȿ'.toUpperCase(), 'Ȿ'.toLowerCase()].map((v)=>v.toUTF8().length)"),
+      nxt_string("2,3,3,2") },
+
     { nxt_string("var a = [], code;"
                  "for (code = 0; code < 65536; code++) {"
                  "    var s = String.fromCharCode(code);"
@@ -5588,7 +5596,6 @@ static njs_unit_test_t  njs_test[] =
                  "        a.push(code);"
                  "} a"),
       nxt_string("304,453,456,459,498,1012,7838,8486,8490,8491") },
-#endif
 
     { nxt_string("'abc'.trim()"),
       nxt_string("abc") },


More information about the nginx-devel mailing list