larskanis / fxruby

FXRuby is an extension module for Ruby that provides an interface to the FOX GUI toolkit.
http://rubydoc.info/gems/fxruby/frames
265 stars 20 forks source link

FXTreeList causes crash when closing app #10

Closed andyschmidt closed 9 years ago

andyschmidt commented 11 years ago

Hi,

after running in circles I finally found the real issue of my crashes.

Crash

F:\Projects\TMP\fxruby_bugs>treelist_dialog.rb
Dialog Canceled
F:/Projects/TMP/fxruby_bugs/treelist_dialog.rb: [BUG] Segmentation fault
ruby 1.9.3p448 (2013-06-27) [i386-mingw32]

-- Control frame information -----------------------------------------------
c:0001 p:0000 s:0002 b:0002 l:00051c d:00051c TOP

-- C level backtrace information -------------------------------------------
C:\Windows\SysWOW64\ntdll.dll(ZwWaitForSingleObject+0x15) [0x77d6f8d1]
C:\Windows\syswow64\kernel32.dll(WaitForSingleObjectEx+0x43) [0x76561194]
C:\Windows\syswow64\kernel32.dll(WaitForSingleObject+0x12) [0x76561148]
C:\Ruby193\bin\msvcrt-ruby191.dll(rb_vm_bugreport+0xf9) [0x62e5bec5]
C:\Ruby193\bin\msvcrt-ruby191.dll(rb_name_err_mesg_new+0x17a) [0x62d3a87a]
C:\Ruby193\bin\msvcrt-ruby191.dll(rb_bug+0x2f) [0x62d3b557]
C:\Ruby193\bin\msvcrt-ruby191.dll(rb_check_safe_str+0x194) [0x62dee898]
 [0x004011e6]
C:\Program Files\ThinkPad\Bluetooth Software\SysWOW64\BtMmHook.dll(SetAndWaitBtM
mHook+0xfac9) [0x10013989]
C:\Windows\SysWOW64\ntdll.dll(RtlKnownExceptionFilter+0xb7) [0x77dc74ff]

Analysis

The crash happens only when

require 'fox16'
include Fox

class CheckBoxTreeList < FXTreeList
  def myelements=(x)
    clearItems
    begin
      x.each do |e|
        node = nil
        levels = e[:name].split('|')

        levels.each_with_index do |l,i|
          item = findItem(l, node, SEARCH_FORWARD|SEARCH_IGNORECASE)
          if item.nil? then
            new_item = FXTreeItem.new( l )
            # [1]
            # crash occurs when appendItem is called with a FXTreeItem object
            item = appendItem(node, new_item)
            # [2]
            # no crash when simple appendItem is used
            #item = appendItem(node, i.to_s)
          end
          node = item
        end
    end

    rescue => bang
        puts bang
        puts bang.backtrace
    end
  end

   def initialize(parent)

    @parent = parent
    super(parent, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|TREELIST_ROOT_BOXES)#|TREELIST_MULTIPLESELECT)

  end
end

class TestGui < FXMainWindow
  class TreeDlg < FXDialogBox
    include Responder

    def initialize(parent, project=nil, prefs={} )

      super(parent, "CheckBox Dialog", DECOR_ALL, :width => 300, :height => 400)

      frame = FXVerticalFrame.new(self, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_GROOVE)
      elements = []
      # [3]
      num_root_nodes = 10
      max_child_nodes = 2
      num_root_nodes.times do |ri|
        max_child_nodes.times do |si|
          name = "root#{ri}|sub#{si}"
          data = name + "-data"
          e = { :name => name, :enabled => false, :data => data }
          elements << e
        end
      end

     @cbtree = CheckBoxTreeList.new(frame)
     # [4]
     @cbtree.myelements = elements

     @cancel_btn = FXButton.new(frame, "Cancel" , :target => self, :selector => FXDialogBox::ID_CANCEL, :opts => BUTTON_NORMAL|LAYOUT_RIGHT)
     FXMAPFUNC(SEL_COMMAND, ID_ACCEPT, :onAccept)
      @accept_btn = FXButton.new(frame, "Accept" ,
        :target => self, :selector => FXDialogBox::ID_ACCEPT,
        :opts => BUTTON_NORMAL|LAYOUT_RIGHT)

    end

    def onAccept(sender, sel, event)
      getApp().stopModal(self, 1)
      self.hide()
      1
    end

  end

  def initialize(app)
    super(app, "Test Application", :width => 800, :height => 600)
    frame = FXVerticalFrame.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_GROOVE)

    FXButton.new(frame, "Open TreeDialog",:opts => FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT).connect(SEL_COMMAND){
      dlg = TreeDlg.new(self)
      if dlg.execute != 0 then
        puts "* Dialog Finished"
      else
        puts "Dialog Canceled"
      end
    }

    FXButton.new(frame, "Exit",:opts => FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT).connect(SEL_COMMAND){ leave }
  end

  def create
    super                  # Create the windows
    show(PLACEMENT_SCREEN) # Make the main window appear
  end

  def leave
    getApp().exit(0)
  end
end

application = FXApp.new('LayoutTester', 'FoxTest')
TestGui.new(application)
application.create
application.run
larskanis commented 11 years ago

Hi Andy,

if the application crashes on exit, it is a garbage collector related bug, in most cases. In theory this could also happen while the application is running, but is typical for the final GC run. Unfortunately the stack trace on Windows is useless, but fortunately your program also crashes on Linux. And on Linux valgrind is a great help. I'll try to fix this soon, but need to do a closer look at the implementation FXTreeList, which is wondrously different to the other list types.

Some background to this: The relation between libfox and Rubys garbage collector is one of the most difficult part in fxruby. In the old days with Ruby-1.8, there was a single great GC run that consisted of the mark phase and the sweep phase immediately afterwards. The final GC run on application exit didn't really work in 1.8. This way fxruby was quite stable.

With newer ruby versions the GC became more and more subtle, which is great for ruby, but hard for fxruby. In fact, libfox it extremely ill-suited for using in a GC'ed environment. libfox often depends on explicit destruction of objects, which is great for C++, but difficult in GC'ed languages. The ownership of Objects is different from case to case and it's even possible that the ownership of objects change while their lifetime.

I've done a lot of work to get the GC'ing stable, but certainly there are still a lot of cases where it could cash (like in this case). Furthermore there are some GC related corner cases (with method overloading), that I can not address, without completely reinventing the current logic (which I'll not do).

Hope that helps.

PS: I'm really amazed about your watobo project! Since I'm doing pentests from time to time, I'll definitely give it a try.

andyschmidt commented 11 years ago

Lars,

thank you very much for the detailed explanation.

If you have any questions regarding watobo just drop me a mail.

Am 28.09.2013 22:11, schrieb Lars Kanis:

Hi Andy,

if the application crashes on exit, it is a garbage collector related bug, in most cases. In theory this could also happen while the application is running, but is typical for the final GC run. Unfortunately the stack trace on Windows is useless, but fortunately your program also crashes on Linux. And on Linux valgrind is a great help. I'll try to fix this soon, but need to do a closer look at the implementation FXTreeList, which is wondrously different to the other list types.

Some background to this: The relation between libfox and Rubys garbage collector is one of the most difficult part in fxruby. In the old days with Ruby-1.8, there was a single great GC run that consisted of the mark phase and the sweep phase immediately afterwards. The final GC run on application exit didn't really work in 1.8. This way fxruby was quite stable.

With newer ruby versions the GC became more and more subtle, which is great for ruby, but hard for fxruby. In fact, libfox it extremely ill-suited for using in a GC'ed environment. libfox often depends on explicit destruction of objects, which is great for C++, but difficult in GC'ed languages. The ownership of Objects is different from case to case and it's even possible that the ownership of objects change while their lifetime.

I've done a lot of work to get the GC'ing stable, but certainly there are still a lot of cases where it could cash (like in this case). Furthermore there are some GC related corner cases (with method overloading), that I can not address, without completely reinventing the current logic (which I'll not do).

Hope that helps.

PS: I'm really amazed about your watobo project! Since I'm doing pentests from time to time, I'll definitely give it a try.

— Reply to this email directly or view it on GitHub https://github.com/larskanis/fxruby/issues/10#issuecomment-25307524.

larskanis commented 9 years ago

This issue should be fixed in fxruby-1.6.29.