CVE-2017-14064: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: Ruby through 2.2.7, 2.3.x through 2.3.4, and 2.4.x through 2.4.1 can expose arbitrary memory during a JSON.generate call. The issues lies in using strdup in ext/json/ext/generator/generator.c, which will stop after encountering a '\0' byte, returning a pointer to a string of length zero, which is not the length stored in space_len.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 9.8
Published: 2017-08-31T17:29Z
Repository: https://github.com/flori/json
Language: C
Files changed: ext/json/ext/generator/generator.c, ext/json/ext/generator/generator.h
Commit date: 2017-04-18 10:38:41 +0200
Commit message: Fix arbitrary heap exposure problem

Diff: diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c
index ef85bb7..2bf8074 100644
--- a/ext/json/ext/generator/generator.c
+++ b/ext/json/ext/generator/generator.c
@@ -306,11 +306,11 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
 
 static char *fstrndup(const char *ptr, unsigned long len) {
   char *result;
   if (len <= 0) return NULL;
   result = ALLOC_N(char, len);
-  memccpy(result, ptr, 0, len);
+  memcpy(result, ptr, len);
   return result;
 }
 
 /*
  * Document-module: JSON::Ext::Generator
@@ -1060,11 +1060,11 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
             state->indent = NULL;
             state->indent_len = 0;
         }
     } else {
         if (state->indent) ruby_xfree(state->indent);
-        state->indent = strdup(RSTRING_PTR(indent));
+        state->indent = fstrndup(RSTRING_PTR(indent), len);
         state->indent_len = len;
     }
     return Qnil;
 }
 
@@ -1098,11 +1098,11 @@ static VALUE cState_space_set(VALUE self, VALUE space)
             state->space = NULL;
             state->space_len = 0;
         }
     } else {
         if (state->space) ruby_xfree(state->space);
-        state->space = strdup(RSTRING_PTR(space));
+        state->space = fstrndup(RSTRING_PTR(space), len);
         state->space_len = len;
     }
     return Qnil;
 }
 
@@ -1134,11 +1134,11 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
             state->space_before = NULL;
             state->space_before_len = 0;
         }
     } else {
         if (state->space_before) ruby_xfree(state->space_before);
-        state->space_before = strdup(RSTRING_PTR(space_before));
+        state->space_before = fstrndup(RSTRING_PTR(space_before), len);
         state->space_before_len = len;
     }
     return Qnil;
 }
 
@@ -1171,11 +1171,11 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
             ruby_xfree(state->object_nl);
             state->object_nl = NULL;
         }
     } else {
         if (state->object_nl) ruby_xfree(state->object_nl);
-        state->object_nl = strdup(RSTRING_PTR(object_nl));
+        state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
         state->object_nl_len = len;
     }
     return Qnil;
 }
 
@@ -1206,11 +1206,11 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
             ruby_xfree(state->array_nl);
             state->array_nl = NULL;
         }
     } else {
         if (state->array_nl) ruby_xfree(state->array_nl);
-        state->array_nl = strdup(RSTRING_PTR(array_nl));
+        state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
         state->array_nl_len = len;
     }
     return Qnil;
 }
 
diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h
index 900b4d5..c367a62 100644
--- a/ext/json/ext/generator/generator.h
+++ b/ext/json/ext/generator/generator.h
@@ -1,9 +1,8 @@
 #ifndef _GENERATOR_H_
 #define _GENERATOR_H_
 
-#include <string.h>
 #include <math.h>
 #include <ctype.h>
 
 #include "ruby.h"
 

Vulnerable code:   memccpy(result, ptr, 0, len);
        state->indent = strdup(RSTRING_PTR(indent));
        state->space = strdup(RSTRING_PTR(space));
        state->space_before = strdup(RSTRING_PTR(space_before));
        state->object_nl = strdup(RSTRING_PTR(object_nl));
        state->array_nl = strdup(RSTRING_PTR(array_nl));
#include <string.h>
Fixed code:   memcpy(result, ptr, len);
        state->indent = fstrndup(RSTRING_PTR(indent), len);
        state->space = fstrndup(RSTRING_PTR(space), len);
        state->space_before = fstrndup(RSTRING_PTR(space_before), len);
        state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
        state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);