Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfaulting in FFI #91

Open
abonander opened this issue Dec 31, 2014 · 9 comments
Open

Segfaulting in FFI #91

abonander opened this issue Dec 31, 2014 · 9 comments
Labels

Comments

@abonander
Copy link
Contributor

#0  0x00007ffff102d000 in ?? ()
#1  0x00007ffff7845aa0 in ft_mem_qalloc ()
   from /usr/lib/x86_64-linux-gnu/libfreetype.so.6
#2  0x00007ffff7845ae8 in ft_mem_alloc ()
   from /usr/lib/x86_64-linux-gnu/libfreetype.so.6
#3  0x00007ffff7845f2d in FT_Stream_New ()
   from /usr/lib/x86_64-linux-gnu/libfreetype.so.6
#4  0x00007ffff7848f50 in FT_Open_Face ()
   from /usr/lib/x86_64-linux-gnu/libfreetype.so.6
#5  0x00007ffff78496cb in FT_New_Face ()
   from /usr/lib/x86_64-linux-gnu/libfreetype.so.6
#6  0x0000555555681299 in freetype::library::Library::new_face (
    self=0x7fffffffd6c8, filepathname=..., face_index=0) at src/library.rs:70
#7  0x00005555555f2521 in opengl_graphics::glyph_cache::GlyphCache::new (
    font=0x7fffffffd730) at src/glyph_cache.rs:39
#8  0x000055555556b4a4 in file_chooser::font () at examples/file_chooser.rs:28
#9  0x0000555555567af7 in file_chooser::main () at examples/file_chooser.rs:11
#10 0x00005555556aef49 in rust_try_inner ()
#11 0x00005555556aef36 in rust_try ()
#12 0x00005555556ac1ff in rt::lang_start::hf694818d7a643d8eflz ()
#13 0x000055555556813f in main ()

I downloaded Freetype's source to get the body of the relevant function (since there doesn't seem to be a debug symbols package available in the Ubuntu/Mint repos ¯_(ツ)_/¯ ):

  FT_BASE_DEF( FT_Pointer )
  ft_mem_qalloc( FT_Memory  memory,
                 FT_Long    size,
                 FT_Error  *p_error )
  {
    FT_Error    error = FT_Err_Ok;
    FT_Pointer  block = NULL;

    if ( size > 0 )
    {
      block = memory->alloc( memory, size );
      if ( block == NULL )
        error = FT_THROW( Out_Of_Memory );
    }
    else if ( size < 0 )
    {
      /* may help catch/prevent security issues */
      error = FT_THROW( Invalid_Argument );
    }

    *p_error = error;
    return block;
  }

The only place a segfault looks possible is the virtual call memory->alloc() which I think would segfault if memory was null, correct?

I can't figure out how to configure a debug build for Freetype or else I'd have tried to debug this further. I mean, I can configure it, but the ./configure script is giving me weird errors.

Is this possibly related to #90?

@abonander abonander added the bug label Dec 31, 2014
@abonander
Copy link
Contributor Author

Can confirm, caused by #90. Keeping the memory variable around in a Box looks like it stopped the segfault:

pub struct Library {
    raw: ffi::FT_Library,
    // Hold onto this because FT_Library references it internally.
    _memory: Box<ffi::FT_MemoryRec>,
}

impl Library {
    pub fn init() -> FtResult<Library> {
        let memory = box ffi::FT_MemoryRec {
            user: 0 as *mut c_void,
            alloc: alloc_library,
            free: free_library,
            realloc: realloc_library,
        };
        unsafe {
            let mut raw = std::ptr::null_mut();

            let err = ffi::FT_New_Library(&*memory, &mut raw);
            if err == ffi::FT_Err_Ok {
                ffi::FT_Add_Default_Modules(raw);
                Ok(Library {
                    raw: raw,
                    _memory: memory,
                })
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }
// ...
}

It's segfaulting in other places still so I'm calling this solution a work in progress.

@abonander
Copy link
Contributor Author

Another backtrace (with Box<ffi::FT_MemoryRec>):

#0  0x0000000000000004 in ?? ()
#1  0x00007ffff7945e63 in FT_Done_Face () from /usr/lib/x86_64-linux-gnu/libfreetype.so.6
#2  0x00005555557c84e1 in freetype::face::Face.Drop::drop (self=0x7fffffffcbc0) at src/face.rs:306
#3  0x00005555555eef76 in freetype..face..Face::glue_drop.12011::h43ed0742c3e9c838 ()
#4  0x00005555555eef16 in opengl_graphics..glyph_cache..GlyphCache::glue_drop.12008::h8fc7e2863385ee97 ()
#5  0x00005555555eddc6 in conrod..ui_context..UiContext::glue_drop.11996::hd0cbea5f3e81f595 ()
#6  0x00005555555fc693 in img_dup::ui::setup::show_setup_ui (settings=...) at src/ui/setup.rs:28
#7  0x000055555567383b in img_dup::ui::show_gui (settings=...) at src/ui/mod.rs:34
#8  0x00005555556758b5 in img_dup::show_gui (settings=...) at src/main.rs:46
#9  0x0000555555674ff1 in img_dup::run () at src/main.rs:60
#10 0x0000555555674e5e in img_dup::main () at src/main.rs:34
#11 0x0000555555887409 in rust_try_inner ()
#12 0x00005555558873f6 in rust_try ()
#13 0x00005555558846bf in rt::lang_start::hf694818d7a643d8eflz ()
#14 0x0000555555674ebf in main ()

Relevant function:

FT_EXPORT_DEF( FT_Error )
  FT_Done_Face( FT_Face  face )
  {
    FT_Error     error;
    FT_Driver    driver;
    FT_Memory    memory;
    FT_ListNode  node;


    error = FT_ERR( Invalid_Face_Handle );
    if ( face && face->driver )
    {
      face->internal->refcount--;
      if ( face->internal->refcount > 0 )
        error = FT_Err_Ok;
      else
      {
        driver = face->driver;
        memory = driver->root.memory;

        /* find face in driver's list */
        node = FT_List_Find( &driver->faces_list, face );
        if ( node )
        {
          /* remove face object from the driver's list */
          FT_List_Remove( &driver->faces_list, node );
          FT_FREE( node );

          /* now destroy the object proper */
          destroy_face( memory, face, driver );
          error = FT_Err_Ok;
        }
      }
    }

    return error;
  }

Not sure what might be segfaulting here. I wish I had debug symbols. Maybe I'll work on a debug build of Freetype tonight.

@abonander
Copy link
Contributor Author

Got Freetype with debug symbols! Things are getting interesting. Backtrace of previous comment, plus some debugging:

(gdb) bt
#0  0x0000000000000004 in ?? ()
#1  0x00007ffff7955863 in FT_Done_Face (face=0x555555c2d350) at /home/austin/Git/freetype/src/base/ftobjs.c:2427
#2  0x00005555557c73c1 in freetype::face::Face.Drop::drop (self=0x7fffffffcae8) at src/face.rs:306
#3  0x00005555555eea16 in freetype..face..Face::glue_drop.12041::h828cae1509744ea4 ()
#4  0x00005555555ee9b6 in opengl_graphics..glyph_cache..GlyphCache::glue_drop.12038::h025e284d8c3c5a72 ()
#5  0x00005555555ed966 in conrod..ui_context..UiContext::glue_drop.12026::hbc8067ffba99cdb2 ()
#6  0x00005555555fc146 in img_dup::ui::setup::show_setup_ui (settings=...) at src/ui/setup.rs:28
#7  0x0000555555672e4b in img_dup::ui::show_gui (settings=...) at src/ui/mod.rs:34
#8  0x0000555555674f50 in img_dup::show_gui (settings=...) at src/main.rs:46
#9  0x0000555555674669 in img_dup::run () at src/main.rs:60
#10 0x00005555556744ce in img_dup::main () at src/main.rs:34
#11 0x0000555555888e69 in rust_try_inner ()
#12 0x0000555555888e56 in rust_try ()
#13 0x0000555555886152 in rt::lang_start::h77048f8efe3428c6rjz ()
#14 0x000055555567452f in main ()
(gdb) frame 1
#1  0x00007ffff7955863 in FT_Done_Face (face=0x555555c2d350) at /home/austin/Git/freetype/src/base/ftobjs.c:2427
2427              FT_FREE( node );
(gdb) print node
$4 = (FT_ListNode) 0x555555efa1c0
(gdb) print *node
$5 = {prev = 0x0, next = 0x0, data = 0x555555c2d350}
(gdb) info locals
error = 35
driver = 0x555555e02cc0
memory = 0x7ffff102d0a0
node = 0x555555efa1c0
(gdb) print *memory
$6 = {user = 0x7ffff1027080, alloc = 0x1, free = 0x4, realloc = 0x5555557c7d20 <library::realloc_library::he6fa3db7668a6ed99qc>}\

I think alloc = 0x1 and free = 0x4 mean something bad happened... It correctly evaluated the deref of realloc, so something's wrong here. I just checked and they're initialized properly, but at some point they get changed...

@jakerr
Copy link

jakerr commented Dec 31, 2014

Nice work @cybergeek94! 👍

@jakerr
Copy link

jakerr commented Dec 31, 2014

I tried the Box solution and continued see crashing as you mentioned. Rc seems to work for me though:

use std::rc::Rc;
//...

pub struct Library {
    raw: ffi::FT_Library,
    _memory: Rc<ffi::FT_MemoryRec>,
}

impl Library {
    pub fn init() -> FtResult<Library> {
        let memory = Rc::new(ffi::FT_MemoryRec {
            user: 0 as *mut c_void,
            alloc: alloc_library,
            free: free_library,
            realloc: realloc_library,
        });
        unsafe {
            let mut raw = std::ptr::null_mut();

            let err = ffi::FT_New_Library(memory.deref(), &mut raw);
            if err == ffi::FT_Err_Ok {
                ffi::FT_Add_Default_Modules(raw);
                Ok(Library {
                    raw: raw,
                    _memory: memory,
                })
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }
// ...

@abonander
Copy link
Contributor Author

@jakerr Looks like you've got it! Still having trouble with file_dialog but I think it's cause I create the opengl_graphics::GlyphCache on one thread and pass it to another. I'll fix that.

You want to issue a PR or should I?

@abonander
Copy link
Contributor Author

Still having trouble in a multithreaded context, even though I created GlyphCache on the other thread:

(gdb) bt
#0  ft_mem_qalloc (memory=memory@entry=0x7fffec423000, size=size@entry=576, p_error=p_error@entry=0x7fffeca4c9dc) at /home/austin/Git/freetype/src/base/ftutil.c:76
#1  0x00007ffff7952428 in ft_mem_alloc (memory=memory@entry=0x7fffec423000, size=size@entry=576, p_error=p_error@entry=0x7fffeca4ca38) at /home/austin/Git/freetype/src/base/ftutil.c:55
#2  0x00007ffff79769d3 in cf2_decoder_parse_charstrings (decoder=decoder@entry=0x7fffeca4cb70, 
    charstring_base=0x7fffec23ea7e "\247}\267\370\371\236\001\364\361\367\375\267\003\367c\367}\025\376\257\n\016\223\225v\371\"\236\001\371G\371*\025\376\256\n\016\367\222\225v\371\"\236\001\372=\371*\025\376\255\n\016\240\213\236\371\004\236\001\371L\371*\025\376\254\n\016\223\213\236\371\004\236\001\367\303\361\003\371G\371*\025\376\253\n\016;\213\261\370ޱ\001\253\245\370\231\242\003\370'\261\025\376\252\n\016\373\374\373\060\244\371\224\244\001\343\327\003\367\277\373\027\025\376\251\n\016\374!\222v\001\213\367\274\003\371\070\004\376\250\n\016\373\374\373\060\244\371\224\244\001\367=\327\003\023", charstring_len=22) at /home/austin/Git/freetype/src/cff/cf2ft.c:319
#3  0x00007ffff797739c in cff_slot_load (glyph=0x7fffdc0086b0, size=0x7fffdc008910, glyph_index=<optimized out>, load_flags=0) at /home/austin/Git/freetype/src/cff/cffgload.c:2839
#4  0x00007ffff7954cb5 in FT_Load_Glyph (face=0x7fffdc0061b0, glyph_index=55, load_flags=<optimized out>) at /home/austin/Git/freetype/src/base/ftobjs.c:731
#5  0x00005555557c727d in freetype::face::Face::load_char (self=0x7fffeca50868, char_code=85, load_flags=...) at src/face.rs:122
#6  0x0000555555701b2d in opengl_graphics::glyph_cache::GlyphCache::load_character (self=0x7fffeca50868, size=24, ch=85) at src/glyph_cache.rs:57
#7  0x000055555570d975 in opengl_graphics::glyph_cache::GlyphCache.graphics::character::CharacterCache<Texture>::character (self=0x7fffeca50868, size=24, ch=85) at src/glyph_cache.rs:108
#8  0x00005555556cce5d in conrod::ui_context::UiContext::get_character (self=0x7fffeca50780, size=24, ch=85) at src/ui_context.rs:192
#9  0x00005555556cccc3 in fn"fn"(8308) (a=0, ch=85) at src/label.rs:19
#10 0x00005555556cca1c in iter::IteratorExt::fold::h16298278109742909170 ()
#11 0x00005555556cc966 in conrod::label::width (uic=0x7fffeca50780, size=24, text=...) at src/label.rs:18
#12 0x00005555556c7cb7 in conrod::rectangle::draw_with_centered_label (win_w=640, win_h=480, graphics=0x7fffeca508d8, uic=0x7fffeca50780, state=Normal, pos=..., dim=..., maybe_frame=..., color=..., text=..., font_size=24, text_color=...)
    at src/rectangle.rs:115
#13 0x00005555556c7002 in conrod::button::ButtonContext<'a>.::draw::Drawable::draw (self=0x7fffeca50170, graphics=0x7fffeca508d8) at src/button.rs:131
#14 0x00005555556b4da0 in file_dialog::draw_dialog_ui (gl=0x7fffeca508d8, uic=0x7fffeca50780, state=0x7fffeca50bf8, buf=0x7fffeca50658) at src/lib.rs:337
#15 0x00005555556b4b6f in fn"fn"(4388) (gl=0x7fffeca508d8) at src/lib.rs:197
#16 0x0000555555715872 in opengl_graphics::gl_back_end::Gl::draw (self=0x7fffeca508d8, viewport=..., f={void (struct Context, struct Gl *)} 0x7fffeca503e8) at src/gl_back_end.rs:378
#17 0x00005555556a42f7 in file_dialog::render_file_dialog (dialog=..., window=..., gl=_3_2) at src/lib.rs:196
#18 0x00005555556a1b3a in fn"fn"(3692) () at src/lib.rs:118
#19 0x00005555556a179e in thunk::Thunk$LT$$LP$$RP$$C$$u{20}R$GT$::new::unboxed_closure.3683 ()
#20 0x00005555556a1686 in thunk::F.Invoke$LT$A$C$$u{20}R$GT$::invoke::h6474481907405875887 ()
#21 0x000055555569ee48 in thunk::Thunk$LT$A$C$$u{20}R$GT$::invoke::h18158447864729569891 ()
#22 0x000055555569fd97 in thread::Builder::spawn_inner::unboxed_closure.3583 ()
#23 0x000055555569fd0f in rt::unwind::try::try_fn::__rust_abi::h7799345596637414855 ()
#24 0x000055555569fcaa in rt::unwind::try::try_fn::h7799345596637414855 ()
#25 0x0000555555889de9 in rust_try_inner ()
#26 0x0000555555889dd6 in rust_try ()
#27 0x000055555569efa0 in rt::unwind::try::h16129879653089677800 ()
#28 0x000055555569d9b9 in thread::Builder::spawn_inner::unboxed_closure.3377 ()
#29 0x00005555556a10f8 in thunk::Thunk$LT$$LP$$RP$$C$$u{20}R$GT$::new::unboxed_closure.3672 ()
#30 0x00005555556a0fed in thunk::F.Invoke$LT$A$C$$u{20}R$GT$::invoke::h13988781369736646004 ()
#31 0x00005555558762d2 in sys::thread::thread_start::h3a59f8e8259d23afK1v ()
#32 0x00007ffff721d182 in start_thread (arg=0x7fffeca52700) at pthread_create.c:312
#33 0x00007ffff6d33efd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
(gdb) print *memory
$11 = {user = 0x75612f656d6f682f, alloc = 0x7375522f6e697473, free = 0x75645f676d692f74, realloc = 0x2e6f677261432f70}

I have no idea what to make of this. The function pointers appear to be initialized properly, but when I try to call them, they segfault:

(gdb) print memory->alloc(0, 0)

Program received signal SIGSEGV, Segmentation fault.
0x7375522f6e697473 in ?? ()
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(at 0x0x7375522f6e697473) will be abandoned.
When the function is done executing, GDB will silently stop.

@abonander
Copy link
Contributor Author

@jakerr @bvssvni

I fixed it. It was easy.

In freetype-sys/src/lib.rs:

struct FT_MemoryRec {
//...
}
unsafe impl Sync for FT_MemoryRec {}

Then revert #90. No more segfaults, even in threaded contexts.

Alternately, we could avoid dealing with FT_MemoryRec altogether and init FT_Library with FT_Init_Freetype. We don't have to worry about establishing our allocators because rustc links malloc to the proper platform implementation automatically.

@bvssvni
Copy link
Member

bvssvni commented Jan 1, 2015

@cybergeek94 Thanks for working on this! Reverted with #92

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants