[njs] Handling zero byte characters inside RegExp pattern strings.

Dmitry Volyntsev xeioex at nginx.com
Tue Jun 4 09:45:46 UTC 2019


details:   https://hg.nginx.org/njs/rev/f57cf6b5233a
branches:  
changeset: 998:f57cf6b5233a
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Jun 04 12:45:06 2019 +0300
description:
Handling zero byte characters inside RegExp pattern strings.

Fixed heap-buffer-overflow in RegExp.prototype.source.
This closes #168 issue on Github.

diffstat:

 njs/njs_regexp.c         |  31 +++++++++++++++++++++++++++----
 njs/test/njs_unit_test.c |  24 ++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 4 deletions(-)

diffs (118 lines):

diff -r 2054b8410a28 -r f57cf6b5233a njs/njs_regexp.c
--- a/njs/njs_regexp.c	Thu May 30 20:05:14 2019 +0300
+++ b/njs/njs_regexp.c	Tue Jun 04 12:45:06 2019 +0300
@@ -207,17 +207,19 @@ njs_regexp_create(njs_vm_t *vm, njs_valu
 
 
 /*
- * 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_
     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_
 
         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_
             if (p == end) {
                 goto done;
             }
+
+            if (*p != '\0') {
+                break;
+            }
+
+            /* Fall through. */
+
+        case '\0':
+            dst = nxt_cpymem(dst, "\\u0000", 6);
+            continue;
         }
 
         *dst++ = *p;
diff -r 2054b8410a28 -r f57cf6b5233a njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Thu May 30 20:05:14 2019 +0300
+++ b/njs/test/njs_unit_test.c	Tue Jun 04 12:45:06 2019 +0300
@@ -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") },
 };
 
 


More information about the nginx-devel mailing list