From ce2bc45ae8679b40e97d6794e4e7a31b68444615 Mon Sep 17 00:00:00 2001 From: nagachika Date: Sat, 3 Feb 2018 02:25:37 +0000 Subject: [PATCH] merge revision(s) 60042,60130,60131: [Backport #13982] ext: adjust indent [ci skip] zlib.c: memory leak in gunzip * ext/zlib/zlib.c (zlib_gunzip): clear zstream to fix memory leak. [ruby-core:83162] [Bug #13982] zlib.c: ensure to free * ext/zlib/zlib.c (zlib_gunzip): gz0 is a structure variable on the stack, no longer valid after exit by an exception. ensure to free instead. [Bug #13982] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_4@62172 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/dbm/dbm.c | 40 ++++++++++++++-------------- ext/etc/etc.c | 2 +- ext/sdbm/_sdbm.c | 56 +++++++++++++++++++-------------------- ext/stringio/stringio.c | 2 +- ext/syslog/syslog.c | 2 +- ext/zlib/zlib.c | 58 ++++++++++++++++++++++++++++++++--------- test/zlib/test_zlib.rb | 8 ++++++ version.h | 2 +- 8 files changed, 105 insertions(+), 65 deletions(-) diff --git a/ext/dbm/dbm.c b/ext/dbm/dbm.c index 07044d6f9f482e..734a4006817446 100644 --- a/ext/dbm/dbm.c +++ b/ext/dbm/dbm.c @@ -191,24 +191,24 @@ fdbm_initialize(int argc, VALUE *argv, VALUE obj) } if (dbm) { - /* - * History of dbm_pagfno() and dbm_dirfno() in ndbm and its compatibles. - * (dbm_pagfno() and dbm_dirfno() is not standardized.) - * - * 1986: 4.3BSD provides ndbm. - * It provides dbm_pagfno() and dbm_dirfno() as macros. - * 1991: gdbm-1.5 provides them as functions. - * They returns a same descriptor. - * (Earlier releases may have the functions too.) - * 1991: Net/2 provides Berkeley DB. - * It doesn't provide dbm_pagfno() and dbm_dirfno(). - * 1992: 4.4BSD Alpha provides Berkeley DB with dbm_dirfno() as a function. - * dbm_pagfno() is a macro as DBM_PAGFNO_NOT_AVAILABLE. - * 1997: Berkeley DB 2.0 is released by Sleepycat Software, Inc. - * It defines dbm_pagfno() and dbm_dirfno() as macros. - * 2011: gdbm-1.9 creates a separate dir file. - * dbm_pagfno() and dbm_dirfno() returns different descriptors. - */ + /* + * History of dbm_pagfno() and dbm_dirfno() in ndbm and its compatibles. + * (dbm_pagfno() and dbm_dirfno() is not standardized.) + * + * 1986: 4.3BSD provides ndbm. + * It provides dbm_pagfno() and dbm_dirfno() as macros. + * 1991: gdbm-1.5 provides them as functions. + * They returns a same descriptor. + * (Earlier releases may have the functions too.) + * 1991: Net/2 provides Berkeley DB. + * It doesn't provide dbm_pagfno() and dbm_dirfno(). + * 1992: 4.4BSD Alpha provides Berkeley DB with dbm_dirfno() as a function. + * dbm_pagfno() is a macro as DBM_PAGFNO_NOT_AVAILABLE. + * 1997: Berkeley DB 2.0 is released by Sleepycat Software, Inc. + * It defines dbm_pagfno() and dbm_dirfno() as macros. + * 2011: gdbm-1.9 creates a separate dir file. + * dbm_pagfno() and dbm_dirfno() returns different descriptors. + */ #if defined(HAVE_DBM_PAGFNO) rb_fd_fix_cloexec(dbm_pagfno(dbm)); #endif @@ -217,8 +217,8 @@ fdbm_initialize(int argc, VALUE *argv, VALUE obj) #endif #if defined(RUBYDBM_DB_HEADER) && defined(HAVE_TYPE_DBC) - /* Disable Berkeley DB error messages such as: - * DB->put: attempt to modify a read-only database */ + /* Disable Berkeley DB error messages such as: + * DB->put: attempt to modify a read-only database */ ((DBC*)dbm)->dbp->set_errfile(((DBC*)dbm)->dbp, NULL); #endif } diff --git a/ext/etc/etc.c b/ext/etc/etc.c index f871d9c2ae4863..46857c62c79492 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -1014,7 +1014,7 @@ etc_nprocessors(VALUE obj) ncpus = etc_nprocessors_affin(); if (ncpus != -1) { - return INT2NUM(ncpus); + return INT2NUM(ncpus); } /* fallback to _SC_NPROCESSORS_ONLN */ #endif diff --git a/ext/sdbm/_sdbm.c b/ext/sdbm/_sdbm.c index 9ff0e7959aab2d..3a42b0e56951d6 100644 --- a/ext/sdbm/_sdbm.c +++ b/ext/sdbm/_sdbm.c @@ -176,24 +176,24 @@ sdbm_open(register char *file, register int flags, register int mode) static int fd_set_cloexec(int fd) { - /* MinGW don't have F_GETFD and FD_CLOEXEC. [ruby-core:40281] */ + /* MinGW don't have F_GETFD and FD_CLOEXEC. [ruby-core:40281] */ #ifdef F_GETFD - int flags, ret; - flags = fcntl(fd, F_GETFD); /* should not fail except EBADF. */ - if (flags == -1) { - return -1; - } - if (2 < fd) { - if (!(flags & FD_CLOEXEC)) { - flags |= FD_CLOEXEC; - ret = fcntl(fd, F_SETFD, flags); - if (ret == -1) { - return -1; - } - } - } + int flags, ret; + flags = fcntl(fd, F_GETFD); /* should not fail except EBADF. */ + if (flags == -1) { + return -1; + } + if (2 < fd) { + if (!(flags & FD_CLOEXEC)) { + flags |= FD_CLOEXEC; + ret = fcntl(fd, F_SETFD, flags); + if (ret == -1) { + return -1; + } + } + } #endif - return 0; + return 0; } DBM * @@ -400,20 +400,20 @@ makroom(register DBM *db, long int hash, int need) */ #if defined _WIN32 - /* - * Fill hole with 0 if made it. - * (hole is NOT read as 0) - */ - oldtail = lseek(db->pagf, 0L, SEEK_END); - memset(zer, 0, PBLKSIZ); - while (OFF_PAG(newp) > oldtail) { - if (lseek(db->pagf, 0L, SEEK_END) < 0 || - write(db->pagf, zer, PBLKSIZ) < 0) { + /* + * Fill hole with 0 if made it. + * (hole is NOT read as 0) + */ + oldtail = lseek(db->pagf, 0L, SEEK_END); + memset(zer, 0, PBLKSIZ); + while (OFF_PAG(newp) > oldtail) { + if (lseek(db->pagf, 0L, SEEK_END) < 0 || + write(db->pagf, zer, PBLKSIZ) < 0) { - return 0; + return 0; + } + oldtail += PBLKSIZ; } - oldtail += PBLKSIZ; - } #endif if (hash & (db->hmask + 1)) { diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 1e464bc9d1c5ce..ffb21c12380341 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -1551,7 +1551,7 @@ strio_external_encoding(VALUE self) static VALUE strio_internal_encoding(VALUE self) { - return Qnil; + return Qnil; } /* diff --git a/ext/syslog/syslog.c b/ext/syslog/syslog.c index 37c9d0bf0c1c9a..317607eeef257d 100644 --- a/ext/syslog/syslog.c +++ b/ext/syslog/syslog.c @@ -506,7 +506,7 @@ void Init_syslog(void) rb_define_syslog_facility(LOG_NEWS); #endif #ifdef LOG_NTP - rb_define_syslog_facility(LOG_NTP); + rb_define_syslog_facility(LOG_NTP); #endif #ifdef LOG_SECURITY rb_define_syslog_facility(LOG_SECURITY); diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 336cb2d0959658..fde85befab5759 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -451,7 +451,7 @@ rb_zlib_adler32(int argc, VALUE *argv, VALUE klass) static VALUE rb_zlib_adler32_combine(VALUE klass, VALUE adler1, VALUE adler2, VALUE len2) { - return ULONG2NUM( + return ULONG2NUM( adler32_combine(NUM2ULONG(adler1), NUM2ULONG(adler2), NUM2LONG(len2))); } #else @@ -489,7 +489,7 @@ rb_zlib_crc32(int argc, VALUE *argv, VALUE klass) static VALUE rb_zlib_crc32_combine(VALUE klass, VALUE crc1, VALUE crc2, VALUE len2) { - return ULONG2NUM( + return ULONG2NUM( crc32_combine(NUM2ULONG(crc1), NUM2ULONG(crc2), NUM2LONG(len2))); } #else @@ -644,7 +644,7 @@ zstream_expand_buffer(struct zstream *z) } else { zstream_expand_buffer_into(z, - ZSTREAM_AVAIL_OUT_STEP_MAX - buf_filled); + ZSTREAM_AVAIL_OUT_STEP_MAX - buf_filled); } } else { @@ -1381,7 +1381,7 @@ rb_zstream_data_type(VALUE obj) static VALUE rb_zstream_adler(VALUE obj) { - return rb_uint2inum(get_zstream(obj)->stream.adler); + return rb_uint2inum(get_zstream(obj)->stream.adler); } /* @@ -2673,7 +2673,7 @@ gzfile_calc_crc(struct gzfile *gz, VALUE str) } else { gz->crc = checksum_long(crc32, gz->crc, (Bytef*)RSTRING_PTR(str) + gz->ungetc, - RSTRING_LEN(str) - gz->ungetc); + RSTRING_LEN(str) - gz->ungetc); gz->ungetc = 0; } } @@ -4245,6 +4245,14 @@ rb_gzreader_external_encoding(VALUE self) return rb_enc_from_encoding(get_gzfile(self)->enc); } +static VALUE +zlib_gzip_ensure(VALUE arg) +{ + struct gzfile *gz = (struct gzfile *)arg; + rb_rescue((VALUE(*)())gz->end, arg, NULL, Qnil); + return Qnil; +} + static void zlib_gzip_end(struct gzfile *gz) { @@ -4257,6 +4265,7 @@ zlib_gzip_end(struct gzfile *gz) #define OPTHASH_GIVEN_P(opts) \ (argc > 0 && !NIL_P((opts) = rb_check_hash_type(argv[argc-1])) && (--argc, 1)) static ID id_level, id_strategy; +static VALUE zlib_gzip_run(VALUE arg); /* * call-seq: @@ -4285,9 +4294,8 @@ zlib_s_gzip(int argc, VALUE *argv, VALUE klass) { struct gzfile gz0; struct gzfile *gz = &gz0; - long len; int err; - VALUE src, opts, level=Qnil, strategy=Qnil; + VALUE src, opts, level=Qnil, strategy=Qnil, args[2]; if (OPTHASH_GIVEN_P(opts)) { ID keyword_ids[2]; @@ -4309,9 +4317,23 @@ zlib_s_gzip(int argc, VALUE *argv, VALUE klass) err = deflateInit2(&gz->z.stream, gz->level, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, ARG_STRATEGY(strategy)); if (err != Z_OK) { + zlib_gzip_end(gz); raise_zlib_error(err, gz->z.stream.msg); } ZSTREAM_READY(&gz->z); + args[0] = (VALUE)gz; + args[1] = src; + return rb_ensure(zlib_gzip_run, (VALUE)args, zlib_gzip_ensure, (VALUE)gz); +} + +static VALUE +zlib_gzip_run(VALUE arg) +{ + VALUE *args = (VALUE *)arg; + struct gzfile *gz = (struct gzfile *)args[0]; + VALUE src = args[1]; + long len; + gzfile_make_header(gz); len = RSTRING_LEN(src); if (len > 0) { @@ -4327,10 +4349,11 @@ static void zlib_gunzip_end(struct gzfile *gz) { gz->z.flags |= ZSTREAM_FLAG_CLOSING; - gzfile_check_footer(gz); zstream_end(&gz->z); } +static VALUE zlib_gunzip_run(VALUE arg); + /* * call-seq: * Zlib.gunzip(src) -> String @@ -4355,7 +4378,6 @@ zlib_gunzip(VALUE klass, VALUE src) struct gzfile gz0; struct gzfile *gz = &gz0; int err; - VALUE dst; StringValue(src); @@ -4367,14 +4389,24 @@ zlib_gunzip(VALUE klass, VALUE src) gz->io = Qundef; gz->z.input = src; ZSTREAM_READY(&gz->z); + return rb_ensure(zlib_gunzip_run, (VALUE)gz, zlib_gzip_ensure, (VALUE)gz); +} + +static VALUE +zlib_gunzip_run(VALUE arg) +{ + struct gzfile *gz = (struct gzfile *)arg; + VALUE dst; + gzfile_read_header(gz); dst = zstream_detach_buffer(&gz->z); gzfile_calc_crc(gz, dst); - if (!ZSTREAM_IS_FINISHED(&gz->z)) { - rb_raise(cGzError, "unexpected end of file"); - } - if (NIL_P(gz->z.input)) + if (!ZSTREAM_IS_FINISHED(&gz->z)) { + rb_raise(cGzError, "unexpected end of file"); + } + if (NIL_P(gz->z.input)) { rb_raise(cNoFooter, "footer is not found"); + } gzfile_check_footer(gz); return dst; } diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index 8ca14dafc8acf1..b3feac2f3b6747 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -1196,5 +1196,13 @@ def test_gunzip src = %w[1f8b080000000000000].pack("H*") assert_raise(Zlib::GzipFile::Error){ Zlib.gunzip(src) } end + + def test_gunzip_no_memory_leak + assert_no_memory_leak(%[-rzlib], "#{<<~"{#"}", "#{<<~'};'}") + d = Zlib.gzip("data") + {# + 10_000.times {Zlib.gunzip(d)} + }; + end end end diff --git a/version.h b/version.h index 78da54bb0b5283..dad6505d5cf60e 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ #define RUBY_VERSION "2.4.4" #define RUBY_RELEASE_DATE "2018-02-03" -#define RUBY_PATCHLEVEL 229 +#define RUBY_PATCHLEVEL 230 #define RUBY_RELEASE_YEAR 2018 #define RUBY_RELEASE_MONTH 2