summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-07-15 20:45:55 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-07-15 20:45:55 +0000
commit192c936e2317286fa428a76a4018c9082d11c828 (patch)
tree3e3545e5eb7871c1aa8635b6861d36b2ba3750f0
parent66e1be60c39e3ab96fc7de01fb950cac9b5bc700 (diff)
* sprintf.c (rb_f_sprintf): more checks for format argument.
[ruby-core:11569], [ruby-core:11570], [ruby-core:11571], [ruby-core:11573] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12803 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog6
-rw-r--r--sprintf.c61
-rw-r--r--test/ruby/test_sprintf.rb33
3 files changed, 74 insertions, 26 deletions
diff --git a/ChangeLog b/ChangeLog
index c54b3d7801..57451b84c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Mon Jul 16 05:45:53 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * sprintf.c (rb_f_sprintf): more checks for format argument.
+ [ruby-core:11569], [ruby-core:11570], [ruby-core:11571],
+ [ruby-core:11573]
+
Mon Jul 16 00:26:10 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
* bignum.c (rb_big_pow): removed invariant variable. [ruby-dev:31236]
diff --git a/sprintf.c b/sprintf.c
index 5aeb03875a..29ebc0e66b 100644
--- a/sprintf.c
+++ b/sprintf.c
@@ -79,6 +79,7 @@ sign_bits(int base, const char *p)
#define FSPACE 16
#define FWIDTH 32
#define FPREC 64
+#define FPREC0 128
#define CHECK(l) do {\
while (blen + (l) >= bsiz) {\
@@ -113,9 +114,7 @@ sign_bits(int base, const char *p)
#define GETNTHARG(nth) \
((nth >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[nth])
-#define GETASTER(val) do { \
- t = p++; \
- n = 0; \
+#define GETNUM(n, val) \
for (; p < end && ISDIGIT(*p); p++) { \
int next_n = 10 * n + (*p - '0'); \
if (next_n / 10 != n) {\
@@ -125,7 +124,12 @@ sign_bits(int base, const char *p)
} \
if (p >= end) { \
rb_raise(rb_eArgError, "malformed format string - %%*[0-9]"); \
- } \
+ }
+
+#define GETASTER(val) do { \
+ t = p++; \
+ n = 0; \
+ GETNUM(n, val); \
if (*p == '$') { \
tmp = GETPOSARG(n); \
} \
@@ -263,6 +267,21 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
VALUE tmp;
VALUE str;
+#define CHECK_FOR_WIDTH(f) \
+ if ((f) & FWIDTH) { \
+ rb_raise(rb_eArgError, "width given twice"); \
+ } \
+ if ((f) & FPREC0) { \
+ rb_raise(rb_eArgError, "width after precision"); \
+ }
+#define CHECK_FOR_FLAGS(f) \
+ if ((f) & FWIDTH) { \
+ rb_raise(rb_eArgError, "flag after width"); \
+ } \
+ if ((f) & FPREC0) { \
+ rb_raise(rb_eArgError, "flag after precision"); \
+ }
+
++argc;
--argv;
if (OBJ_TAINTED(fmt)) tainted = 1;
@@ -299,43 +318,40 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
break;
case ' ':
+ CHECK_FOR_FLAGS(flags);
flags |= FSPACE;
p++;
goto retry;
case '#':
+ CHECK_FOR_FLAGS(flags);
flags |= FSHARP;
p++;
goto retry;
case '+':
+ CHECK_FOR_FLAGS(flags);
flags |= FPLUS;
p++;
goto retry;
case '-':
+ CHECK_FOR_FLAGS(flags);
flags |= FMINUS;
p++;
goto retry;
case '0':
+ CHECK_FOR_FLAGS(flags);
flags |= FZERO;
p++;
goto retry;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
+ CHECK_FOR_WIDTH(flags);
n = 0;
- for (; p < end && ISDIGIT(*p); p++) {
- int next_n = 10 * n + (*p - '0');
- if (next_n / 10 != n) {
- rb_raise(rb_eArgError, "width too big");
- }
- n = next_n;
- }
- if (p >= end) {
- rb_raise(rb_eArgError, "malformed format string - %%[0-9]");
- }
+ GETNUM(n, width);
if (*p == '$') {
if (nextvalue != Qundef) {
rb_raise(rb_eArgError, "value given twice - %d$", n);
@@ -349,9 +365,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
goto retry;
case '*':
- if (flags & FWIDTH) {
- rb_raise(rb_eArgError, "width given twice");
- }
+ CHECK_FOR_WIDTH(flags);
flags |= FWIDTH;
GETASTER(width);
if (width < 0) {
@@ -362,10 +376,10 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
goto retry;
case '.':
- if (flags & FPREC) {
+ if (flags & FPREC0) {
rb_raise(rb_eArgError, "precision given twice");
}
- flags |= FPREC;
+ flags |= FPREC|FPREC0;
prec = 0;
p++;
@@ -378,17 +392,12 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
goto retry;
}
- for (; p < end && ISDIGIT(*p); p++) {
- prec = 10 * prec + (*p - '0');
- }
- if (p >= end) {
- rb_raise(rb_eArgError, "malformed format string - %%.[0-9]");
- }
+ GETNUM(prec, precision);
goto retry;
case '\n':
- p--;
case '\0':
+ p--;
case '%':
if (flags != FNONE) {
rb_raise(rb_eArgError, "illegal format character - %%");
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index c72eadb218..5e0b763e99 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -138,4 +138,37 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("-Inf ", sprintf("%- 08f", -inf))
assert_equal("-0000Inf", sprintf("%+ 08f", -inf))
end
+
+ def test_invalid
+ # [ruby-core:11569]
+
+ # Star precision before star width:
+ assert_raise(ArgumentError) {sprintf("%.**d", 5, 10, 1)}
+
+ # Precision before flags and width:
+ assert_raise(ArgumentError) {sprintf("%.5+05d", 5)}
+ assert_raise(ArgumentError) {sprintf("%.5 5d", 5)}
+
+ # Overriding a star width with a numeric one:
+ assert_raise(ArgumentError) {sprintf("%*1s", 5, 1)}
+
+ # Width before flags:
+ assert_raise(ArgumentError) {sprintf("%5+0d", 1)}
+ assert_raise(ArgumentError) {sprintf("%5 0d", 1)}
+
+ # Specifying width multiple times:
+ assert_raise(ArgumentError) {sprintf("%50+30+20+10+5d", 5)}
+ assert_raise(ArgumentError) {sprintf("%50 30 20 10 5d", 5)}
+
+ # [ruby-core:11570]
+ # Specifying the precision multiple times with negative star arguments:
+ assert_raise(ArgumentError) {sprintf("%.*.*.*.*f", -1, -1, -1, 5, 1)}
+
+ # [ruby-core:11571]
+ # Null bytes after percent signs are removed:
+ assert_equal("%\0x hello", sprintf("%\0x hello"))
+
+ # [ruby-core:11573]
+ assert_raise(ArgumentError) {sprintf("%.25555555555555555555555555555555555555s", "hello")}
+ end
end