]> git.kaiwu.me - njs.git/commitdiff
Handling zero byte characters inside RegExp pattern strings.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 4 Jun 2019 09:45:06 +0000 (12:45 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Tue, 4 Jun 2019 09:45:06 +0000 (12:45 +0300)
Fixed heap-buffer-overflow in RegExp.prototype.source.
This closes #168 issue on Github.

njs/njs_regexp.c
njs/test/njs_unit_test.c

index 03a3003a597808aea73c948a638f3819d2f4583e..a78025f41dd116d3691e21f0990fd48589068902 100644 (file)
@@ -207,17 +207,19 @@ njs_regexp_create(njs_vm_t *vm, njs_value_t *value, u_char *start,
 
 
 /*
- * PCRE with PCRE_JAVASCRIPT_COMPAT flag rejects regexps with
+ * 1) PCRE with PCRE_JAVASCRIPT_COMPAT flag rejects regexps with
  * lone closing square brackets as invalid.  Whereas according
  * to ES6: 11.8.5 it is a valid regexp expression.
  *
+ * 2) escaping zero byte characters as "\u0000".
+ *
  * Escaping it here as a workaround.
  */
 
 nxt_inline njs_ret_t
 njs_regexp_escape(njs_vm_t *vm, nxt_str_t *text)
 {
-    size_t      brackets;
+    size_t      brackets, zeros;
     u_char      *p, *dst, *start, *end;
     nxt_bool_t  in;
 
@@ -225,6 +227,7 @@ njs_regexp_escape(njs_vm_t *vm, nxt_str_t *text)
     end = text->start + text->length;
 
     in = 0;
+    zeros = 0;
     brackets = 0;
 
     for (p = start; p < end; p++) {
@@ -244,14 +247,24 @@ njs_regexp_escape(njs_vm_t *vm, nxt_str_t *text)
 
         case '\\':
             p++;
+
+            if (p == end || *p != '\0') {
+                break;
+            }
+
+            /* Fall through. */
+
+        case '\0':
+            zeros++;
+            break;
         }
     }
 
-    if (!brackets) {
+    if (!brackets && !zeros) {
         return NXT_OK;
     }
 
-    text->length = text->length + brackets;
+    text->length = text->length + brackets + zeros * nxt_length("\\u0000");
 
     text->start = nxt_mp_alloc(vm->mem_pool, text->length);
     if (nxt_slow_path(text->start == NULL)) {
@@ -283,6 +296,16 @@ njs_regexp_escape(njs_vm_t *vm, nxt_str_t *text)
             if (p == end) {
                 goto done;
             }
+
+            if (*p != '\0') {
+                break;
+            }
+
+            /* Fall through. */
+
+        case '\0':
+            dst = nxt_cpymem(dst, "\\u0000", 6);
+            continue;
         }
 
         *dst++ = *p;
index 204281cb0369f1cb1bb279ca8861a6def0d380ac..e400ad52a0b62a618a556c428b1bdc181189549b 100644 (file)
@@ -5564,6 +5564,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("/]cd/"),
       nxt_string("/\\]cd/") },
 
+    { nxt_string("RegExp('\\\\0').source[1]"),
+      nxt_string("0") },
+
     { nxt_string("']'.match(/]/)"),
       nxt_string("]") },
 
@@ -12812,6 +12815,27 @@ static njs_unit_test_t  njs_regexp_test[] =
 
     { nxt_string("/[\\uFDE0-\\uFFFD]/g; export default 1"),
       nxt_string("SyntaxError: Illegal export statement in 1") },
+
+    { nxt_string("RegExp(RegExp('\x00]]')).test('\x00]]')"),
+      nxt_string("true") },
+
+    { nxt_string("RegExp('\0').test('\0')"),
+      nxt_string("true") },
+
+    { nxt_string("RegExp('\x00').test('\0')"),
+      nxt_string("true") },
+
+    { nxt_string("RegExp('\x00\\\\x00').source"),
+      nxt_string("\\u0000\\x00") },
+
+    { nxt_string("/\\\0/"),
+      nxt_string("/\\\\u0000/") },
+
+    { nxt_string("RegExp('\\\\\\0').source"),
+      nxt_string("\\\\u0000") },
+
+    { nxt_string("RegExp('[\0]').test('\0')"),
+      nxt_string("true") },
 };