From e751293e10d5ecbb2ef43a61b9c153a1fc4f0304 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Mon, 28 Mar 2011 10:03:16 +0100 Subject: [PATCH] ruby: Don't segfault if callbacks throw exceptions (RHBZ#664558). (Thanks Chris Lalancette). See: https://bugzilla.redhat.com/show_bug.cgi?id=664558#c6 --- generator/generator_ruby.ml | 49 ++++++++++++++++++++++++++++++++++++++----- ruby/tests/tc_rhbz664558c6.rb | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 ruby/tests/tc_rhbz664558c6.rb diff --git a/generator/generator_ruby.ml b/generator/generator_ruby.ml index 84d10e0..7c8788d 100644 --- a/generator/generator_ruby.ml +++ b/generator/generator_ruby.ml @@ -55,6 +55,8 @@ static VALUE c_guestfs; /* guestfs_h handle */ static VALUE e_Error; /* used for all errors */ static void ruby_event_callback_wrapper (guestfs_h *g, void *data, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len); +static VALUE ruby_event_callback_wrapper_wrapper (VALUE argv); +static VALUE ruby_event_callback_handle_exception (VALUE not_used, VALUE exn); static VALUE **get_all_event_callbacks (guestfs_h *g, size_t *len_rtn); static void @@ -212,7 +214,7 @@ ruby_event_callback_wrapper (guestfs_h *g, const uint64_t *array, size_t array_len) { size_t i; - VALUE eventv, event_handlev, bufv, arrayv; + VALUE eventv, event_handlev, bufv, arrayv, argv; eventv = ULL2NUM (event); event_handlev = INT2NUM (event_handle); @@ -223,12 +225,49 @@ ruby_event_callback_wrapper (guestfs_h *g, for (i = 0; i < array_len; ++i) rb_ary_push (arrayv, ULL2NUM (array[i])); - /* XXX If the Ruby callback raises any sort of exception then - * it causes the process to segfault. I don't understand how - * to catch exceptions here. + /* Wrap up the arguments in an array which will be unpacked + * and passed as multiple arguments. This is a crap limitation + * of rb_rescue. + * http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/~poffice/mail/ruby-talk/65698 */ - rb_funcall (*(VALUE *) data, rb_intern (\"call\"), 4, + argv = rb_ary_new2 (5); + rb_ary_store (argv, 0, * (VALUE *) data /* function */); + rb_ary_store (argv, 1, eventv); + rb_ary_store (argv, 2, event_handlev); + rb_ary_store (argv, 3, bufv); + rb_ary_store (argv, 4, arrayv); + + rb_rescue (ruby_event_callback_wrapper_wrapper, argv, + ruby_event_callback_handle_exception, Qnil); +} + +static VALUE +ruby_event_callback_wrapper_wrapper (VALUE argv) +{ + VALUE fn, eventv, event_handlev, bufv, arrayv; + + fn = rb_ary_entry (argv, 0); + eventv = rb_ary_entry (argv, 1); + event_handlev = rb_ary_entry (argv, 2); + bufv = rb_ary_entry (argv, 3); + arrayv = rb_ary_entry (argv, 4); + + rb_funcall (fn, rb_intern (\"call\"), 4, eventv, event_handlev, bufv, arrayv); + + return Qnil; +} + +static VALUE +ruby_event_callback_handle_exception (VALUE not_used, VALUE exn) +{ + /* Callbacks aren't supposed to throw exceptions. The best we + * can do is to print the error. + */ + fprintf (stderr, \"libguestfs: exception in callback: %%s\", + StringValueCStr (exn)); + + return Qnil; } static VALUE ** diff --git a/ruby/tests/tc_rhbz664558c6.rb b/ruby/tests/tc_rhbz664558c6.rb new file mode 100644 index 0000000..ec5cf49 --- /dev/null +++ b/ruby/tests/tc_rhbz664558c6.rb @@ -0,0 +1,48 @@ +# libguestfs Ruby bindings -*- ruby -*- +# Copyright (C) 2011 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Test that throwing an exception in a callback doesn't cause +# the interpreter to segfault. See: +# https://bugzilla.redhat.com/show_bug.cgi?id=664558#c6 + +require 'test/unit' +$:.unshift(File::join(File::dirname(__FILE__), "..", "lib")) +$:.unshift(File::join(File::dirname(__FILE__), "..", "ext", "guestfs")) +require 'guestfs' + +class TestLoad < Test::Unit::TestCase + def test_rhbz664558c6 + g = Guestfs::create() + + close_invoked = 0 + close = Proc.new {| event, event_handle, buf, array | + close_invoked += 1 + # Raising an exception used to cause the interpreter to + # segfault. It should just cause an error message to be + # printed on stderr. + raise "ignore this error" + } + g.set_event_callback(close, Guestfs::EVENT_CLOSE) + + # This should call the close callback. + g.close() + + if close_invoked != 1 + raise "close_invoked should be 1" + end + end +end -- 1.8.3.1