Don't write directly to Ruby String object storage
authorWincent Colaiuta <win@wincent.com>
Sun, 6 Dec 2009 13:26:11 +0000 (14:26 +0100)
committerWincent Colaiuta <win@wincent.com>
Sun, 6 Dec 2009 13:26:11 +0000 (14:26 +0100)
Rather than overwriting part of the buffer that Ruby uses to store the
template String, create a temporary C string for use with mkdtemp.

In practice, there has never been a problem with this kind of direct
manipulation of Ruby String storage with any version of Ruby that I have
tested, but just to be future-proof we err on the side of caution here.

Signed-off-by: Wincent Colaiuta <win@wincent.com>
ext/mkdtemp.c

index 1e25da656d2a0cd1d0f3d3a12c500a4deb4934ae..c334a192fc1f6026a33229b195c7939f4fa7462e 100644 (file)
@@ -35,16 +35,31 @@ Enterprise Linux it states that the template suffix "must be XXXXXX".
 static VALUE dir_mkdtemp_m(int argc, VALUE *argv, VALUE self)
 {
     VALUE template;
+    char *c_template;
     char *path;
+
+    /* process argument */
     if (rb_scan_args(argc, argv, "01", &template) == 0) /* check for 0 mandatory arguments, 1 optional argument */
         template = Qnil;                                /* default to nil if no argument passed */
     if (NIL_P(template))
         template = rb_str_new2("/tmp/temp.XXXXXX");     /* fallback to this template if passed nil */
     SafeStringValue(template);                          /* raises if template is tainted and SAFE level > 0 */
     template = StringValue(template);                   /* duck typing support */
-    path = mkdtemp(RSTRING_PTR(template));
+
+    /* create temporary storage */
+    c_template = malloc(RSTRING_LEN(template) + 1);
+    if (!c_template)
+        rb_raise(rb_eNoMemError, "failed to allocate %d bytes of template storage", RSTRING_LEN(template) + 1);
+    strncpy(c_template, RSTRING_PTR(template), RSTRING_LEN(template));
+    c_template[RSTRING_LEN(template)] = 0;              /* NUL-terminate */
+
+    /* fill out template */
+    path = mkdtemp(c_template);
+    if (path)
+        template = rb_str_new2(path);
+    free(c_template);
     if (path == NULL)
-        rb_raise(rb_eSystemCallError, "mkdtemp failed (error: %d)", errno);
+        rb_raise(rb_eSystemCallError, "mkdtemp failed (error #%d: %s)", errno, strerror(errno));
     return template;
 }