Skip to content

Commit e531cd4

Browse files
committed
mruby-io, mruby-socket: convert an argument before reading its Integer
Three methods hand mrb_integer() a value that is not known to be an Integer, so the bits of some other object are read as one: - IO#ungetbyte takes anything that is not a String as an Integer. ungetbyte(nil) pushes back "\x00", ungetbyte([0x41]) pushes back a byte of the Array, and :A or Object.new are accepted too. - BasicSocket#setsockopt calls mrb_ensure_int_type() on the level and drops the converted value, then reads the original. A Float level such as Socket::SOL_SOCKET.to_f becomes an unrelated number and setsockopt(2) fails with EINVAL. - IO#gets drops the result of the same conversion for a non-String first argument, so gets(5.0) raises TypeError ("Float cannot be converted to String") instead of reading up to 5 bytes. Use mrb_as_int(), the conversion mrb_get_args() does for "i", in all three. IO#ungetbyte(nil) now pushes nothing back and returns nil, as it does in CRuby, and an object that is neither an Integer nor a String raises TypeError.
1 parent d474475 commit e531cd4

4 files changed

Lines changed: 39 additions & 8 deletions

File tree

‎mrbgems/mruby-io/src/io.c‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,14 +1915,17 @@ io_ungetbyte(mrb_state *mrb, mrb_value io)
19151915
mrb_value c = mrb_get_arg1(mrb);
19161916
unsigned char byte_val;
19171917

1918+
if (mrb_nil_p(c)) {
1919+
return mrb_nil_value(); /* nil pushes nothing back, as in CRuby */
1920+
}
19181921
if (mrb_string_p(c)) {
19191922
if (RSTRING_LEN(c) == 0) {
19201923
return mrb_nil_value(); /* Empty string, do nothing */
19211924
}
19221925
byte_val = (unsigned char)RSTRING_PTR(c)[0];
19231926
}
19241927
else {
1925-
mrb_int val = mrb_integer(c);
1928+
mrb_int val = mrb_as_int(mrb, c);
19261929
byte_val = (unsigned char)(val & 0xff);
19271930
}
19281931

@@ -2131,14 +2134,11 @@ io_gets(mrb_state *mrb, mrb_value io)
21312134
if (mrb_nil_p(rs)) {
21322135
rs_given = FALSE;
21332136
}
2134-
else if (mrb_integer_p(rs)) {
2135-
limit = mrb_integer(rs);
2137+
else if (!mrb_string_p(rs)) {
2138+
limit = mrb_as_int(mrb, rs);
21362139
limit_given = TRUE;
21372140
rs = mrb_nil_value();
21382141
}
2139-
else if (!mrb_string_p(rs)) {
2140-
mrb_ensure_int_type(mrb, rs);
2141-
}
21422142
}
21432143
}
21442144
if (rs_given) {

‎mrbgems/mruby-io/test/io.rb‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,26 @@ def to_s
503503
end
504504
end
505505

506+
assert('IO#ungetbyte') do
507+
io = IO.new(IO.sysopen($mrbtest_io_rfname))
508+
assert_equal 'm', io.getc
509+
assert_nil io.ungetbyte(0x4d)
510+
assert_equal 'M', io.getc
511+
assert_nil io.ungetbyte(-0xbf)
512+
assert_equal 'A', io.getc
513+
assert_nil io.ungetbyte("B")
514+
assert_equal 'B', io.getc
515+
516+
# nil pushes nothing back, and an object that is neither an Integer nor
517+
# a String is refused rather than read as an Integer
518+
assert_nil io.ungetbyte(nil)
519+
assert_raise(TypeError) { io.ungetbyte([0x41]) }
520+
assert_raise(TypeError) { io.ungetbyte(:A) }
521+
assert_raise(TypeError) { io.ungetbyte(Object.new) }
522+
assert_equal 'r', io.getc
523+
io.close
524+
end
525+
506526
assert('IO#isatty') do
507527
unless MRubyIOTestUtil.win?
508528
begin
@@ -577,6 +597,8 @@ def io._buf
577597
# gets with limit
578598
io.pos = 0
579599
assert_equal $mrbtest_io_msg[0, 5], io.gets(5), "gets with limit"
600+
io.pos = 0
601+
assert_equal $mrbtest_io_msg[0, 5], io.gets(5.0), "gets with a Float limit"
580602

581603
# gets with rs
582604
io.pos = 0

‎mrbgems/mruby-socket/src/socket.c‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,8 +806,7 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
806806
mrb_int argc = mrb_get_args(mrb, "o|io", &so, &optname, &optval);
807807

808808
if (argc == 3) {
809-
mrb_ensure_int_type(mrb, so);
810-
level = mrb_integer(so);
809+
level = mrb_as_int(mrb, so);
811810
if (mrb_string_p(optval)) {
812811
/* that's good */
813812
}

‎mrbgems/mruby-socket/test/basicsocket.rb‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,13 @@
1515
assert_equal(BasicSocket.do_not_reverse_lookup, false)
1616
BasicSocket.do_not_reverse_lookup = true
1717
end
18+
19+
assert('BasicSocket#setsockopt with a Float level') do
20+
s = UDPSocket.new
21+
begin
22+
s.setsockopt(Socket::SOL_SOCKET.to_f, Socket::SO_REUSEADDR, true)
23+
assert_true s.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR).bool
24+
ensure
25+
s.close
26+
end
27+
end

0 commit comments

Comments
 (0)