From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS32748 69.162.128.0/18 X-Spam-Status: No, score=-1.2 required=3.0 tests=BAYES_00,RCVD_IN_BRBL_LASTEXT, RCVD_IN_MSPIKE_BL,RCVD_IN_MSPIKE_ZBI,RCVD_IN_XBL,SPF_FAIL,SPF_HELO_FAIL, TO_EQ_FM_DOM_SPF_FAIL shortcircuit=no autolearn=no autolearn_force=no version=3.4.0 Received: from 80x24.org (readme.tor.camolist.com [69.162.139.9]) by dcvr.yhbt.net (Postfix) with ESMTP id 40C971FF1E for ; Mon, 30 Jan 2017 22:03:31 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] io.c (rb_io_syswrite): avoid leaving garbage after write Date: Mon, 30 Jan 2017 22:03:29 +0000 Message-Id: <20170130220329.3240-1-e@80x24.org> List-Id: As with IO#write, IO#syswrite also generates garbage which can be harmful in hand-coded read-write loops. * io.c (swrite_arg, swrite_do, swrite_end): new (rb_io_syswrite): use new functions to cleanup garbage [ruby-core:78898] [Bug #13085] --- io.c | 45 ++++++++++++++++++++++++++++++++++++--------- test/ruby/test_io.rb | 4 +++- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/io.c b/io.c index d03519be92..57ad592daf 100644 --- a/io.c +++ b/io.c @@ -4745,6 +4745,34 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io) return OFFT2NUM(pos); } +struct swrite_arg { + VALUE orig; + VALUE tmp; + rb_io_t *fptr; +}; + +static VALUE +swrite_do(VALUE arg) +{ + struct swrite_arg *sa = (struct swrite_arg *)arg; + const char *ptr; + long len; + + RSTRING_GETMEM(sa->tmp, ptr, len); + + return (VALUE)rb_write_internal(sa->fptr->fd, ptr, len); +} + +static VALUE +swrite_end(VALUE arg) +{ + struct swrite_arg *sa = (struct swrite_arg *)arg; + + rb_str_tmp_frozen_release(sa->orig, sa->tmp); + + return Qfalse; +} + /* * call-seq: * ios.syswrite(string) -> integer @@ -4761,26 +4789,25 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io) static VALUE rb_io_syswrite(VALUE io, VALUE str) { - rb_io_t *fptr; + struct swrite_arg sa; long n; if (!RB_TYPE_P(str, T_STRING)) str = rb_obj_as_string(str); io = GetWriteIO(io); - GetOpenFile(io, fptr); - rb_io_check_writable(fptr); - - str = rb_str_new_frozen(str); + GetOpenFile(io, sa.fptr); + rb_io_check_writable(sa.fptr); - if (fptr->wbuf.len) { + if (sa.fptr->wbuf.len) { rb_warn("syswrite for buffered IO"); } - n = rb_write_internal(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str)); - RB_GC_GUARD(str); + sa.orig = str; + sa.tmp = rb_str_tmp_frozen_acquire(str); + n = (long)rb_ensure(swrite_do, (VALUE)&sa, swrite_end, (VALUE)&sa); - if (n == -1) rb_sys_fail_path(fptr->pathv); + if (n == -1) rb_sys_fail_path(sa.fptr->pathv); return LONG2FIX(n); } diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index c501edd0ab..3cc410881c 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -3512,11 +3512,13 @@ def test_write_no_garbage with_pipe do |r, w| before = ObjectSpace.count_objects(res)[:T_STRING] n = w.write(buf) + s = w.syswrite(buf) after = ObjectSpace.count_objects(res)[:T_STRING] assert_equal before, after, 'no strings left over after write [ruby-core:78898] [Bug #13085]' assert_not_predicate buf, :frozen?, 'no inadvertant freeze' - assert_equal buf.bytesize, n, 'wrote expected size' + assert_equal buf.bytesize, n, 'write wrote expected size' + assert_equal s, n, 'syswrite wrote expected size' end end end -- EW