]> git.kaiwu.me - njs.git/commitdiff
Fixed String.toLowerCase() and String.toUpperCase().
authorValentin Bartenev <vbart@nginx.com>
Sat, 27 Jul 2019 14:03:02 +0000 (17:03 +0300)
committerValentin Bartenev <vbart@nginx.com>
Sat, 27 Jul 2019 14:03:02 +0000 (17:03 +0300)
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.

njs/njs_string.c
njs/test/njs_unit_test.c

index 55134f4c730d6feb3ca6f687906378c2f49e6459..50fc497bfedb0a96f86191f0f8f9c52634094d6c 100644 (file)
@@ -2171,23 +2171,23 @@ njs_string_prototype_to_lower_case(njs_vm_t *vm, njs_value_t *args,
     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;
-    }
+    if (string.length == 0 || string.length == string.size) {
+        /* Byte or ASCII string. */
 
-    p = start;
-    s = string.start;
-    size = string.size;
+        p = njs_string_alloc(vm, &vm->retval, string.size, string.length);
+        if (nxt_slow_path(p == NULL)) {
+            return NXT_ERROR;
+        }
 
-    if (string.length == 0 || string.length == size) {
-        /* Byte or ASCII string. */
+        s = string.start;
+        size = string.size;
 
         while (size != 0) {
             *p++ = nxt_lower_case(*s++);
@@ -2196,11 +2196,29 @@ njs_string_prototype_to_lower_case(njs_vm_t *vm, njs_value_t *args,
 
     } 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,23 +2238,23 @@ njs_string_prototype_to_upper_case(njs_vm_t *vm, njs_value_t *args,
     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;
-    }
+    if (string.length == 0 || string.length == string.size) {
+        /* Byte or ASCII string. */
 
-    p = start;
-    s = string.start;
-    size = string.size;
+        p = njs_string_alloc(vm, &vm->retval, string.size, string.length);
+        if (nxt_slow_path(p == NULL)) {
+            return NXT_ERROR;
+        }
 
-    if (string.length == 0 || string.length == size) {
-        /* Byte or ASCII string. */
+        s = string.start;
+        size = string.size;
 
         while (size != 0) {
             *p++ = nxt_upper_case(*s++);
@@ -2245,11 +2263,29 @@ njs_string_prototype_to_upper_case(njs_vm_t *vm, njs_value_t *args,
 
     } 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--;
         }
     }
index 5f774e48c3519cd7dc37db462b1da3c14199ae9a..6bab1984f6492dc755cd222967c0c2716b622ddc 100644 (file)
@@ -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") },