Remove implicit conversion using `to_int` method.

The ISO standard does not include implicit type conversion using
`to_int`. This implicit conversion often causes vulnerability.
There will be no more attacks like #4120.

In addition, we have added internal convenience method `__to_int` which
does type check and conversion (from floats).
parent 8b437546
......@@ -716,7 +716,6 @@ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
* @return [mrb_value] The newly duplicated object.
*/
MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj);
MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method);
/**
* Returns true if obj responds to the given method. If the method was defined for that
......
......@@ -13,10 +13,9 @@ module Enumerable
# a.drop(3) #=> [4, 5, 0]
def drop(n)
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
n = n.__to_int
raise ArgumentError, "attempt to drop negative size" if n < 0
n = n.to_int
ary = []
self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
ary
......@@ -57,8 +56,8 @@ module Enumerable
# a.take(3) #=> [1, 2, 3]
def take(n)
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
i = n.to_int
n = n.__to_int
i = n.to_i
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
......@@ -113,12 +112,12 @@ module Enumerable
# [8, 9, 10]
def each_cons(n, &block)
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
n = n.__to_int
raise ArgumentError, "invalid size" if n <= 0
return to_enum(:each_cons,n) unless block
ary = []
n = n.to_int
n = n.to_i
self.each do |*val|
ary.shift if ary.size == n
ary << val.__svalue
......@@ -141,12 +140,12 @@ module Enumerable
# [10]
def each_slice(n, &block)
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
n = n.__to_int
raise ArgumentError, "invalid slice size" if n <= 0
return to_enum(:each_slice,n) unless block
ary = []
n = n.to_int
n = n.to_i
self.each do |*val|
ary << val.__svalue
if ary.size == n
......@@ -225,9 +224,7 @@ module Enumerable
end
return nil
when 1
n = args[0]
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
i = n.to_int
i = args[0].__to_int
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
......@@ -675,13 +672,7 @@ module Enumerable
if nv.nil?
n = -1
else
unless nv.respond_to?(:to_int)
raise TypeError, "no implicit conversion of #{nv.class} into Integer"
end
n = nv.to_int
unless n.kind_of?(Integer)
raise TypeError, "no implicit conversion of #{nv.class} into Integer"
end
n = nv.__to_int
return nil if n <= 0
end
......
......@@ -157,12 +157,10 @@ class Enumerator
def with_index(offset=0, &block)
return to_enum :with_index, offset unless block
offset = if offset.nil?
0
elsif offset.respond_to?(:to_int)
offset.to_int
if offset.nil?
offset = 0
else
raise TypeError, "no implicit conversion of #{offset.class} into Integer"
offset = offset.__to_int
end
n = offset - 1
......
......@@ -55,12 +55,6 @@ assert 'Enumerator#with_index' do
assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
end
assert 'Enumerator#with_index nonnum offset' do
s = Object.new
def s.to_int; 1 end
assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a)
end
assert 'Enumerator#with_index string offset' do
assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
end
......
......@@ -93,9 +93,8 @@ mrb_f_method(mrb_state *mrb, mrb_value self)
* (<code>0</code>, <code>0b</code>, and <code>0x</code>) are honored.
* In any case, strings should be strictly conformed to numeric
* representation. This behavior is different from that of
* <code>String#to_i</code>. Non string values will be converted using
* <code>to_int</code>, and <code>to_i</code>. Passing <code>nil</code>
* raises a TypeError.
* <code>String#to_i</code>. Non string values will be treated as integers.
* Passing <code>nil</code> raises a TypeError.
*
* Integer(123.999) #=> 123
* Integer("0x1a") #=> 26
......
......@@ -2,13 +2,10 @@
#include <mruby.h>
static inline mrb_int
to_int(mrb_value x)
to_int(mrb_state *mrb, mrb_value x)
{
double f;
if (mrb_fixnum_p(x)) return mrb_fixnum(x);
f = mrb_float(x);
return (mrb_int)f;
x = mrb_to_int(mrb, x);
return mrb_fixnum(x);
}
/*
......@@ -28,7 +25,7 @@ mrb_int_chr(mrb_state *mrb, mrb_value x)
mrb_int chr;
char c;
chr = to_int(x);
chr = to_int(mrb, x);
if (chr >= (1 << CHAR_BIT)) {
mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
}
......@@ -48,8 +45,8 @@ mrb_int_allbits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
n = to_int(self);
mrb_get_args(mrb, "i", &m);
n = to_int(mrb, self);
return mrb_bool_value((n & m) == m);
}
......@@ -64,8 +61,8 @@ mrb_int_anybits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
n = to_int(self);
mrb_get_args(mrb, "i", &m);
n = to_int(mrb, self);
return mrb_bool_value((n & m) != 0);
}
......@@ -80,8 +77,8 @@ mrb_int_nobits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
n = to_int(self);
mrb_get_args(mrb, "i", &m);
n = to_int(mrb, self);
return mrb_bool_value((n & m) == 0);
}
......
......@@ -1124,14 +1124,16 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
o = mrb_ary_ref(mrb, ary, aidx);
if (type == PACK_TYPE_INTEGER) {
o = mrb_to_int(mrb, o);
}
#ifndef MRB_WITHOUT_FLOAT
} else if (type == PACK_TYPE_FLOAT) {
else if (type == PACK_TYPE_FLOAT) {
if (!mrb_float_p(o)) {
mrb_float f = mrb_to_flo(mrb, o);
o = mrb_float_value(mrb, f);
}
}
#endif
} else if (type == PACK_TYPE_STRING) {
else if (type == PACK_TYPE_STRING) {
if (!mrb_string_p(o)) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
}
......
......@@ -79,12 +79,12 @@ get_opt(mrb_state* mrb)
mrb_get_args(mrb, "|o", &arg);
if (!mrb_nil_p(arg)) {
arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
if (mrb_nil_p(arg)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
}
if (mrb_fixnum(arg) < 0) {
arg = mrb_fixnum_value(0 - mrb_fixnum(arg));
mrb_int i;
arg = mrb_to_int(mrb, arg);
i = mrb_fixnum(arg);
if (i < 0) {
arg = mrb_fixnum_value(0 - i);
}
}
return arg;
......
......@@ -74,15 +74,3 @@ assert('Array#shuffle!(random)') do
ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2
end
assert('Array#sample checks input length after reading arguments') do
$ary = [1, 2, 3]
class ArrayChange
def to_i
$ary << 4
4
end
end
assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
end
......@@ -15,10 +15,7 @@ class Range
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
nv = args[0]
raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
n = nv.to_int
raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
n = nv.__to_int
raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
ary = []
each do |i|
......
......@@ -31,18 +31,6 @@ assert('String#setbyte') do
assert_equal("Hello", str1)
end
assert("String#setbyte raises IndexError if arg conversion resizes String") do
$s = "01234\n"
class Tmp
def to_i
$s.chomp! ''
95
end
end
tmp = Tmp.new
assert_raise(IndexError) { $s.setbyte(5, tmp) }
end
assert('String#byteslice') do
str1 = "hello"
assert_equal("e", str1.byteslice(1))
......
......@@ -66,7 +66,7 @@ class Array
#
# ISO 15.2.12.5.15
def initialize(size=0, obj=nil, &block)
raise TypeError, "expected Integer for 1st argument" unless size.kind_of? Integral
size = size.__to_int
raise ArgumentError, "negative array size" if size < 0
self.clear
......
......@@ -830,6 +830,7 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
}
mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
void
mrb_init_kernel(mrb_state *mrb)
{
......@@ -871,6 +872,7 @@ mrb_init_kernel(mrb_state *mrb)
mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ANY()); /* 15.3.1.3.43 */
mrb_define_method(mrb, krn, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */
mrb_define_method(mrb, krn, "__case_eqq", mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */
mrb_define_method(mrb, krn, "__to_int", mrb_to_int, MRB_ARGS_NONE()); /* internal */
mrb_define_method(mrb, krn, "class_defined?", mrb_krn_class_defined, MRB_ARGS_REQ(1));
......
......@@ -674,7 +674,6 @@ flo_round(mrb_state *mrb, mrb_value num)
/*
* call-seq:
* flt.to_i -> integer
* flt.to_int -> integer
* flt.truncate -> integer
*
* Returns <i>flt</i> truncated to an <code>Integer</code>.
......@@ -714,7 +713,6 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
/*
* call-seq:
* int.to_i -> integer
* int.to_int -> integer
*
* As <i>int</i> is already an <code>Integer</code>, all these
* methods simply return the receiver.
......@@ -1513,7 +1511,6 @@ mrb_init_numeric(mrb_state *mrb)
MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM);
mrb_undef_class_method(mrb, integer, "new");
mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
#ifndef MRB_WITHOUT_FLOAT
mrb_define_method(mrb, integer, "ceil", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */
mrb_define_method(mrb, integer, "floor", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */
......@@ -1565,7 +1562,6 @@ mrb_init_numeric(mrb_state *mrb)
mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */
mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */
mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE());
mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */
mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1));
mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
......
......@@ -322,19 +322,6 @@ convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *metho
return mrb_funcall_argv(mrb, val, m, 0, 0);
}
MRB_API mrb_value
mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method)
{
mrb_value v;
if (mrb_fixnum_p(val)) return val;
v = convert_type(mrb, val, "Integer", method, FALSE);
if (mrb_nil_p(v) || !mrb_fixnum_p(v)) {
return mrb_nil_value();
}
return v;
}
MRB_API mrb_value
mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
{
......@@ -505,25 +492,22 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
return FALSE;
}
static mrb_value
mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method)
{
mrb_value v;
if (mrb_fixnum_p(val)) return val;
v = convert_type(mrb, val, "Integer", method, TRUE);
if (!mrb_obj_is_kind_of(mrb, v, mrb->fixnum_class)) {
mrb_value type = inspect_type(mrb, val);
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)",
type, type, mrb_str_new_cstr(mrb, method), inspect_type(mrb, v));
}
return v;
}
MRB_API mrb_value
mrb_to_int(mrb_state *mrb, mrb_value val)
{
return mrb_to_integer(mrb, val, "to_int");
if (!mrb_fixnum_p(val)) {
mrb_value type;
#ifndef MRB_WITHOUT_FLOAT
if (mrb_float_p(val)) {
return mrb_flo_to_fixnum(mrb, val);
}
#endif
type = inspect_type(mrb, val);
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer", type);
}
return val;
}
MRB_API mrb_value
......@@ -533,18 +517,12 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
if (mrb_nil_p(val)) {
if (base != 0) goto arg_error;
mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
}
switch (mrb_type(val)) {
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
if (base != 0) goto arg_error;
else {
mrb_float f = mrb_float(val);
if (FIXABLE_FLOAT(f)) {
break;
}
}
return mrb_flo_to_fixnum(mrb, val);
#endif
......@@ -568,11 +546,8 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
arg_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
}
tmp = convert_type(mrb, val, "Integer", "to_int", FALSE);
if (mrb_nil_p(tmp) || !mrb_fixnum_p(tmp)) {
tmp = mrb_to_integer(mrb, val, "to_i");
}
return tmp;
/* to raise TypeError */
return mrb_to_int(mrb, val);
}
MRB_API mrb_value
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment