From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS4713 221.184.0.0/13 X-Spam-Status: No, score=-3.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id 912C21F463 for ; Fri, 27 Sep 2019 09:31:11 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 68CC8120A71; Fri, 27 Sep 2019 18:31:01 +0900 (JST) Received: from xtrwkhkc.outbound-mail.sendgrid.net (xtrwkhkc.outbound-mail.sendgrid.net [167.89.16.28]) by neon.ruby-lang.org (Postfix) with ESMTPS id 75F241208CF for ; Fri, 27 Sep 2019 18:30:57 +0900 (JST) Received: by filter0142p3mdw1.sendgrid.net with SMTP id filter0142p3mdw1-17004-5D8DD6D3-2E 2019-09-27 09:30:59.348048687 +0000 UTC m=+34730.297036564 Received: from herokuapp.com (unknown [3.91.96.212]) by ismtpd0040p1iad2.sendgrid.net (SG) with ESMTP id nkNu08mRQ6ivlvWXKoiaTw for ; Fri, 27 Sep 2019 09:30:59.307 +0000 (UTC) Date: Fri, 27 Sep 2019 09:30:59 +0000 (UTC) From: iv@altlinux.org Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 70684 X-Redmine-Project: ruby-trunk X-Redmine-Issue-Id: 16184 X-Redmine-Issue-Author: iv-m X-Redmine-Sender: iv-m X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-SG-EID: =?us-ascii?Q?WfYseQ1RardbWxw=2FdWWhetV1CqQcnZYCD6ZOQJG8UaAiNcJ01vJrT5M1+UsUtd?= =?us-ascii?Q?fcPVY9kOnB3Ft0g+VFSLCKdAtUUoTeI6lMa46AR?= =?us-ascii?Q?VB093SGJhWvoagTqG2QsT0l+=2FlIr673=2FENk4oDq?= =?us-ascii?Q?j4vK6Uh=2F5MPAHj7wh9Wad05EzyS9+22fp+k=2F0Gg?= =?us-ascii?Q?LijEhaZq8fP9ecj3CDSrZNGvMubKTbtq99g=3D=3D?= To: ruby-core@ruby-lang.org X-ML-Name: ruby-core X-Mail-Count: 95129 Subject: [ruby-core:95129] [Ruby master Bug#16184] Entry persists in catch table even though its labels were removed, which may cause [BUG] X-BeenThere: ruby-core@ruby-lang.org X-Mailman-Version: 2.1.15 Precedence: list Reply-To: Ruby developers List-Id: Ruby developers List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #16184 has been updated by iv-m (Ivan Melnikov). File crush.log added > Could you post a reproducer that reliably crashes Ruby? I guess this is not really possible to crash Ruby reliably via this issue. Here is a reproducer that crashes on my MIPS32 LE machine one out of 20 times or so: ```ruby puts "BEGIN" if false begin require "some_mad_stuff" rescue LoadError puts "no mad stuff loaded" end end throw "foo" ``` I'm attaching the interpreter output from one of the crashes. As far as I understood, here's what happens. When `"foo"` is thrown from the last line of the reproducer, exception table is considered. The exception table contains only one entry. The `start` and `end` fields of the entry come from labels which position were never initialized, since the rescue block was removed before the compilation process reached `iseq_set_sequence`. So, the `start` and `end` fields of the cache table entry contain (somewhat processed) garbage from unitialized memory. Here are a few examples from machine where crush sometimes happens: ``` $ cat disasm.rb is = RubyVM::InstructionSequence.compile_file(ARGV[0]) puts is.disasm() $ for x in `seq 1 10`; do ruby disasm.rb test3.rb | grep ' rescue'; done | catch type: rescue st: 0000 ed: 0001 sp: -002 cont: 0000 | catch type: rescue st: 7168352 ed: 7168492 sp: -002 cont: 0000 | catch type: rescue st: 0000 ed: 0001 sp: -002 cont: 0000 | catch type: rescue st: 9085280 ed: 9085420 sp: -002 cont: 0000 | catch type: rescue st: 0000 ed: 0001 sp: -002 cont: 0000 | catch type: rescue st: 0000 ed: 0001 sp: -002 cont: 0000 | catch type: rescue st: 0000 ed: 0001 sp: -002 cont: 0000 | catch type: rescue st: 0000 ed: 0001 sp: -002 cont: 0000 | catch type: rescue st: 7070048 ed: 7070188 sp: -002 cont: 0000 | catch type: rescue st: 10264928 ed: 10265068 sp: -002 cont: 0000 ``` To actually crash Ruby, `start` should very close to 0, and `end` field should be large enough. In this case, Ruby considers the entry applicable and goes to the position `cont` field provides (which also comes from uninitialized memory), and sets `sp` to some very large value ( `(usigned int) -1` or `(usigned int) -2`), and segfaults. So whether Ruby crashes or not depends on the contents of the memory where labels are allocated by `compile_rescue` function. On my x86_64 machine it always has zeroes for some reason, so crashing Ruby on it is probably impossible. I'm just "lucky" to have a machine where this memory actually contains some garbage. ---------------------------------------- Bug #16184: Entry persists in catch table even though its labels were removed, which may cause [BUG] https://bugs.ruby-lang.org/issues/16184#change-81764 * Author: iv-m (Ivan Melnikov) * Status: Feedback * Priority: Normal * Assignee: * Target version: * ruby -v: ruby 2.5.5p157 (2019-03-15) [mipsel-linux] * Backport: 2.5: UNKNOWN, 2.6: UNKNOWN ---------------------------------------- When `remove_unreachable_chunk` removes the code from within a rescue block, the catch table entry corresponding the block is not removed. Here is a simple reproducer (tested with ruby 2.5.5): ``` ruby puts "BEGIN" if false begin require "some_mad_stuff" rescue LoadError puts "no mad stuff loaded" end end puts "END" ``` Here is the corresponding disasm: ``` == disasm: #@test2.rb:1 (1,0)-(12,10)>====================== == catch table | catch type: rescue st: 11022376 ed: 11022516 sp: -002 cont: 0000 == disasm: #@test2.rb:7 (7,2)-(9,2)>============== local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 1] "\#$!" 0000 getlocal_OP__WC__0 "\#$!" ( 7) 0002 getinlinecache 9, 0005 getconstant :LoadError 0007 setinlinecache 0009 checkmatch 3 0011 branchunless 20 0013 putself ( 8)[Li] 0014 putstring "no mad stuff loaded" 0016 opt_send_without_block , 0019 leave ( 7) 0020 getlocal_OP__WC__0 "\#$!" 0022 throw 0 | catch type: retry st: 11022516 ed: 0000 sp: -001 cont: 11022376 |------------------------------------------------------------------------ 0000 putself ( 2)[Li] 0001 putstring "BEGIN" 0003 opt_send_without_block , 0006 pop 0007 putself ( 12)[Li] 0008 putstring "END" 0010 opt_send_without_block , 0013 leave ``` The interesting line here is: ``` catch type: rescue st: 11022376 ed: 11022516 sp: -002 cont: 0000 ``` As the instruction corresponding the `begin..rescue` block were optimized away, the `sp` filed of the continue label was still -1 (or -2) and `position`s of start, end and continue labels are never initialized. When any exception happens in the remaining code (requires a bit more complex reproducer, happens in real life), this may cause an interpreter to segfault. I've discovered this issue when building net-scp gem, which has this `if false; require` pattern in its Rakefile: https://github.com/net-ssh/net-scp/blob/v2.0.0/Rakefile . Interpreter reproducibly segfaults when trying to run it on my MIPS32 LE machine. ---Files-------------------------------- ruby-2.5.5-alt-fix-crash-on-mipsel.patch (372 Bytes) crush.log (8.14 KB) -- https://bugs.ruby-lang.org/