[前][次][番号順一覧][スレッド一覧]

ruby-changes:16533

From: usa <ko1@a...>
Date: Fri, 2 Jul 2010 23:31:47 +0900 (JST)
Subject: [ruby-changes:16533] Ruby:r28525 (trunk): * io.c (argf_inplace_mode_set): prohibits an assignment of a tainted

usa	2010-07-02 23:31:26 +0900 (Fri, 02 Jul 2010)

  New Revision: 28525

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=28525

  Log:
    * io.c (argf_inplace_mode_set): prohibits an assignment of a tainted
      value.
    
    * file.c (ruby_find_basename, ruby_find_extname): split from
      rb_file_s_basename() and rb_file_s_extname().
    
    * util.c (ruby_add_suffix): support arbitrary length of the suffix
      to get rid of the potential buffer overflow.
      reported by tarui.

  Modified files:
    trunk/ChangeLog
    trunk/file.c
    trunk/io.c
    trunk/util.c

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 28524)
+++ ChangeLog	(revision 28525)
@@ -1,3 +1,15 @@
+Fri Jul  2 23:30:23 2010  NAKAMURA Usaku  <usa@r...>
+
+	* io.c (argf_inplace_mode_set): prohibits an assignment of a tainted
+	  value.
+
+	* file.c (ruby_find_basename, ruby_find_extname): split from
+	  rb_file_s_basename() and rb_file_s_extname().
+
+	* util.c (ruby_add_suffix): support arbitrary length of the suffix
+	  to get rid of the potential buffer overflow.
+	  reported by tarui.
+
 Fri Jul  2 05:31:51 2010  Tanaka Akira  <akr@f...>
 
 	* numeric.c (rb_num2long): accept LONG_MAX < x < LONG_MAX+1 and
Index: io.c
===================================================================
--- io.c	(revision 28524)
+++ io.c	(revision 28525)
@@ -9558,6 +9558,9 @@
 static VALUE
 argf_inplace_mode_set(VALUE argf, VALUE val)
 {
+    if (rb_safe_level() >= 1 && OBJ_TAINTED(val))
+	rb_insecure_operation();
+
     if (!RTEST(val)) {
 	if (ARGF.inplace) free(ARGF.inplace);
 	ARGF.inplace = 0;
Index: util.c
===================================================================
--- util.c	(revision 28524)
+++ util.c	(revision 28525)
@@ -258,77 +258,85 @@
 static const char suffix1[] = ".$$$";
 static const char suffix2[] = ".~~~";
 
-#define ext (&buf[1000])
-
 #define strEQ(s1,s2) (strcmp(s1,s2) == 0)
 
+extern const char *ruby_find_basename(const char *, long *, long *);
+extern const char *ruby_find_extname(const char *, long *);
+
 void
 ruby_add_suffix(VALUE str, const char *suffix)
 {
     int baselen;
     int extlen = strlen(suffix);
-    char *s, *t, *p;
+    char *p, *q;
     long slen;
     char buf[1024];
-    char *const bufend = buf + sizeof(buf);
+    const char *name;
+    const char *ext;
+    long len;
 
-    if (RSTRING_LEN(str) > 1000)
-        rb_fatal("Cannot do inplace edit on long filename (%ld characters)",
-		 RSTRING_LEN(str));
+    name = StringValueCStr(str);
+    slen = strlen(name);
+    if (slen > sizeof(buf) - 1)
+	rb_fatal("Cannot do inplace edit on long filename (%ld characters)",
+		 slen);
 
-#if defined(__CYGWIN32__) || defined(_WIN32)
     /* Style 0 */
-    slen = RSTRING_LEN(str);
     rb_str_cat(str, suffix, extlen);
     if (valid_filename(RSTRING_PTR(str))) return;
 
     /* Fooey, style 0 failed.  Fix str before continuing. */
     rb_str_resize(str, slen);
-#endif
+    name = StringValueCStr(str);
+    ext = ruby_find_extname(name, &len);
 
-    slen = extlen;
-    t = buf; baselen = 0; s = RSTRING_PTR(str);
-    while ((*t = *s) && *s != '.') {
-	baselen++;
-	if (*s == '\\' || *s == '/') baselen = 0;
-	s++; t++;
-    }
-    p = t;
-
-    t = ext; extlen = 0;
-    while ((*t++ = *s++) != 0) extlen++;
-    if (extlen == 0) { ext[0] = '.'; ext[1] = 0; extlen++; }
-
     if (*suffix == '.') {        /* Style 1 */
-        if (strEQ(ext, suffix)) goto fallback;
-	strlcpy(p, suffix, bufend - p);
+	if (ext) {
+	    if (strEQ(ext, suffix)) goto fallback;
+	    slen = ext - name;
+	}
+	rb_str_resize(str, slen);
+	rb_str_cat(str, suffix, extlen);
     }
-    else if (suffix[1] == '\0') {  /* Style 2 */
-        if (extlen < 4) {
-	    ext[extlen] = *suffix;
-	    ext[++extlen] = '\0';
-        }
-	else if (baselen < 8) {
-	    *p++ = *suffix;
+    else {
+	strncpy(buf, name, slen);
+	if (ext)
+	    p = buf + (ext - name);
+	else
+	    p = buf + slen;
+	p[len] = '\0';
+	if (suffix[1] == '\0') {  /* Style 2 */
+	    if (len <= 3) {
+		p[len] = *suffix;
+		p[++len] = '\0';
+	    }
+	    else if ((q = (char *)ruby_find_basename(buf, &baselen, 0)) &&
+		     baselen < 8) {
+		q += baselen;
+		*q++ = *suffix;
+		if (ext) {
+		    strncpy(q, ext, ext - name);
+		    q[ext - name + 1] = '\0';
+		}
+		else
+		    *q = '\0';
+	    }
+	    else if (len == 4 && p[3] != *suffix)
+		p[3] = *suffix;
+	    else if (baselen == 8 && q[7] != *suffix)
+		q[7] = *suffix;
+	    else
+		goto fallback;
 	}
-	else if (ext[3] != *suffix) {
-	    ext[3] = *suffix;
+	else { /* Style 3:  Panic */
+	  fallback:
+	    (void)memcpy(p, !ext || strEQ(ext, suffix1) ? suffix2 : suffix1, 5);
 	}
-	else if (buf[7] != *suffix) {
-	    buf[7] = *suffix;
-	}
-	else goto fallback;
-	strlcpy(p, ext, bufend - p);
     }
-    else { /* Style 3:  Panic */
-fallback:
-	(void)memcpy(p, strEQ(ext, suffix1) ? suffix2 : suffix1, 5);
-    }
     rb_str_resize(str, strlen(buf));
     memcpy(RSTRING_PTR(str), buf, RSTRING_LEN(str));
 }
 
-#if defined(__CYGWIN32__) || defined(_WIN32)
 static int
 valid_filename(const char *s)
 {
@@ -350,7 +358,6 @@
     return 0;
 }
 #endif
-#endif
 
 
 /* mm.c */
Index: file.c
===================================================================
--- file.c	(revision 28524)
+++ file.c	(revision 28525)
@@ -3429,42 +3429,15 @@
     return 0;
 }
 
-/*
- *  call-seq:
- *     File.basename(file_name [, suffix] )  ->  base_name
- *
- *  Returns the last component of the filename given in <i>file_name</i>,
- *  which must be formed using forward slashes (``<code>/</code>'')
- *  regardless of the separator used on the local file system. If
- *  <i>suffix</i> is given and present at the end of <i>file_name</i>,
- *  it is removed.
- *
- *     File.basename("/home/gumby/work/ruby.rb")          #=> "ruby.rb"
- *     File.basename("/home/gumby/work/ruby.rb", ".rb")   #=> "ruby"
- */
-
-static VALUE
-rb_file_s_basename(int argc, VALUE *argv)
+const char *
+ruby_find_basename(const char *name, long *len, long *ext)
 {
-    VALUE fname, fext, basename;
-    const char *name, *p;
+    const char *p;
 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
     const char *root;
 #endif
-    long f, n;
+    long f, n = -1;
 
-    if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
-	rb_encoding *enc;
-	StringValue(fext);
-	if (!rb_enc_asciicompat(enc = rb_enc_get(fext))) {
-	    rb_raise(rb_eEncCompatError, "ascii incompatible character encodings: %s",
-		     rb_enc_name(enc));
-	}
-    }
-    FilePathStringValue(fname);
-    if (!NIL_P(fext)) rb_enc_check(fname, fext);
-    if (RSTRING_LEN(fname) == 0 || !*(name = RSTRING_PTR(fname)))
-	return rb_str_new_shared(fname);
     name = skipprefix(name);
 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
     root = name;
@@ -3503,11 +3476,57 @@
 #else
 	n = chompdirsep(p) - p;
 #endif
+    }
+
+    if (len)
+	*len = f;
+    if (ext)
+	*ext = n;
+    return p;
+}
+
+/*
+ *  call-seq:
+ *     File.basename(file_name [, suffix] )  ->  base_name
+ *
+ *  Returns the last component of the filename given in <i>file_name</i>,
+ *  which must be formed using forward slashes (``<code>/</code>'')
+ *  regardless of the separator used on the local file system. If
+ *  <i>suffix</i> is given and present at the end of <i>file_name</i>,
+ *  it is removed.
+ *
+ *     File.basename("/home/gumby/work/ruby.rb")          #=> "ruby.rb"
+ *     File.basename("/home/gumby/work/ruby.rb", ".rb")   #=> "ruby"
+ */
+
+static VALUE
+rb_file_s_basename(int argc, VALUE *argv)
+{
+    VALUE fname, fext, basename;
+    const char *name, *p;
+    long f, n;
+
+    if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
+	rb_encoding *enc;
+	StringValue(fext);
+	if (!rb_enc_asciicompat(enc = rb_enc_get(fext))) {
+	    rb_raise(rb_eEncCompatError, "ascii incompatible character encodings: %s",
+		     rb_enc_name(enc));
+	}
+    }
+    FilePathStringValue(fname);
+    if (!NIL_P(fext)) rb_enc_check(fname, fext);
+    if (RSTRING_LEN(fname) == 0 || !*(name = RSTRING_PTR(fname)))
+	return rb_str_new_shared(fname);
+
+    p = ruby_find_basename(name, &f, &n);
+    if (n >= 0) {
 	if (NIL_P(fext) || !(f = rmext(p, n, StringValueCStr(fext)))) {
 	    f = n;
 	}
 	if (f == RSTRING_LEN(fname)) return rb_str_new_shared(fname);
     }
+
     basename = rb_str_new(p, f);
     rb_enc_copy(basename, fname);
     OBJ_INFECT(basename, fname);
@@ -3573,27 +3592,22 @@
 }
 
 /*
- *  call-seq:
- *     File.extname(path)  ->  string
+ * accept a String, and return the pointer of the extension.
+ * if len is passed, set the length of extension to it.
+ * returned pointer is in ``name'' or NULL.
+ *                 returns   *len
+ *   no dot        NULL      0
+ *   dotfile       top       0
+ *   end with dot  dot       1
+ *   .ext          dot       len of .ext
+ *   .ext:stream   dot       len of .ext without :stream (NT only)
  *
- *  Returns the extension (the portion of file name in <i>path</i>
- *  after the period).
- *
- *     File.extname("test.rb")         #=> ".rb"
- *     File.extname("a/b/d/test.rb")   #=> ".rb"
- *     File.extname("test")            #=> ""
- *     File.extname(".profile")        #=> ""
- *
  */
-
-static VALUE
-rb_file_s_extname(VALUE klass, VALUE fname)
+const char *
+ruby_find_extname(const char *name, long *len)
 {
-    const char *name, *p, *e;
-    VALUE extname;
+    const char *p, *e;
 
-    FilePathStringValue(fname);
-    name = StringValueCStr(fname);
     p = strrdirsep(name);	/* get the last path component */
     if (!p)
 	p = name;
@@ -3629,9 +3643,46 @@
 	    break;
 	p = CharNext(p);
     }
-    if (!e || e == name || e+1 == p)	/* no dot, or the only dot is first or end? */
+
+    if (len) {
+	/* no dot, or the only dot is first or end? */
+	if (!e || e == name)
+	    *len = 0;
+	else if (e+1 == p)
+	    *len = 1;
+	else
+	    *len = p - e;
+    }
+    return e;
+}
+
+/*
+ *  call-seq:
+ *     File.extname(path)  ->  string
+ *
+ *  Returns the extension (the portion of file name in <i>path</i>
+ *  after the period).
+ *
+ *     File.extname("test.rb")         #=> ".rb"
+ *     File.extname("a/b/d/test.rb")   #=> ".rb"
+ *     File.extname("test")            #=> ""
+ *     File.extname(".profile")        #=> ""
+ *
+ */
+
+static VALUE
+rb_file_s_extname(VALUE klass, VALUE fname)
+{
+    const char *name, *e;
+    long len;
+    VALUE extname;
+
+    FilePathStringValue(fname);
+    name = StringValueCStr(fname);
+    e = ruby_find_extname(name, &len);
+    if (len <= 1)
 	return rb_str_new(0, 0);
-    extname = rb_str_new(e, p - e);	/* keep the dot, too! */
+    extname = rb_str_new(e, len);	/* keep the dot, too! */
     rb_enc_copy(extname, fname);
     OBJ_INFECT(extname, fname);
     return extname;

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]