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

Dropping BitmapGlyph created by Glyph::to_bitmap invalidates the Glyph and causes segfault #233

Closed
inseo-oh opened this issue Dec 3, 2020 · 5 comments

Comments

@inseo-oh
Copy link

inseo-oh commented Dec 3, 2020

I just created BitmapGlyph using to_bitmap method in Glyph struct, and doing so caused segmentation fault.
After looking at GDB backtrace, it seemed like something bad happened during one of Drop implementations.

I did notice that to_bitmap() calls C function FT_Glyph_To_Bitmap, which modifies current glyph, but then to_bitmap treats pointer to the self's raw glyph as if it was newly created object. Then BitmapGlyph's Drop impl deallocates using its raw glyph pointer, which points to the same object as Glyph's raw glyph, making Glyph's raw glyph no longer valid.

Freetype-rs version

0.26.0

Code to reproduce(1)

This segfaults during drop().

fn main() {
    use freetype::{Library, RenderMode};

    let lib = Library::init().unwrap();
    let face = lib.new_face("test.ttf", 0).unwrap();
    face.set_char_size(40 * 64, 0, 50, 0).unwrap();
    face.load_char('A' as usize, freetype::face::LoadFlag::RENDER)
        .unwrap();
    {
        let glyph_slot = face.glyph();
        {
            let glyph = glyph_slot.get_glyph().unwrap();
            {
                let bitmap_glyph = glyph.to_bitmap(RenderMode::Normal, None).unwrap();
                println!("drop()ping bitmap_glyph");
                drop(bitmap_glyph);
                println!("drop()ped bitmap_glyph");
            }
            println!("drop()ping glyph");
            drop(glyph);
            println!("drop()ped glyph");
        }
        println!("drop()ping glyph_slot");
        drop(glyph_slot);
        println!("drop()ped glyph_slot");
    }
}

Above code outputs:

    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/freetype-rs-segfault`
drop()ping bitmap_glyph
drop()ped bitmap_glyph
drop()ping glyph
Segmentation fault (core dumped)

And here's backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7eda102 in FT_Done_Glyph () from /usr/lib/libfreetype.so.6
(gdb) bt
#0  0x00007ffff7eda102 in FT_Done_Glyph () from /usr/lib/libfreetype.so.6
#1  0x000055555555d9ab in freetype::glyph::{{impl}}::drop (self=0x7fffffffe138)
    at /home/kun/.cargo/registry/src/github.com-1ecc6299db9ec823/freetype-rs-0.26.0/src/glyph.rs:170
#2  0x000055555555ab1f in core::ptr::drop_in_place<freetype::glyph::Glyph> ()
    at /home/kun/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:175
#3  0x000055555555b8b8 in core::mem::drop<freetype::glyph::Glyph> (_x=...)
    at /home/kun/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/mem/mod.rs:881
#4  0x000055555555b1fb in freetype_rs_segfault::main () at src/main.rs:20

Code to reproduce(2)

Similarly, calling to_bitmap again also causes segfault.

fn main() {
    use freetype::{Library, RenderMode};

    let lib = Library::init().unwrap();
    let face = lib.new_face("test.ttf", 0).unwrap();
    face.set_char_size(40 * 64, 0, 50, 0).unwrap();
    face.load_char('A' as usize, freetype::face::LoadFlag::RENDER)
        .unwrap();
    {
        let glyph_slot = face.glyph();
        {
            let glyph = glyph_slot.get_glyph().unwrap();
            println!("bitmap_glyph 1");
            {
                let bitmap_glyph = glyph.to_bitmap(RenderMode::Normal, None).unwrap();
                drop(bitmap_glyph);
            }
            println!("bitmap_glyph 2");
            {
                let bitmap_glyph = glyph.to_bitmap(RenderMode::Normal, None).unwrap();
                drop(bitmap_glyph);
            }
            println!("successful");
        }
    }
}

Output:

    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/freetype-rs-segfault`
bitmap_glyph 1
bitmap_glyph 2
Segmentation fault (core dumped)

Backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7eda3fd in FT_Glyph_To_Bitmap () from /usr/lib/libfreetype.so.6
(gdb) bt
#0  0x00007ffff7eda3fd in FT_Glyph_To_Bitmap () from /usr/lib/libfreetype.so.6
#1  0x000055555555d812 in freetype::glyph::Glyph::to_bitmap (self=0x7fffffffe250, 
    render_mode=freetype::render_mode::RenderMode::Normal, origin=...)
    at /home/kun/.cargo/registry/src/github.com-1ecc6299db9ec823/freetype-rs-0.26.0/src/glyph.rs:83
#2  0x000055555555b1af in freetype_rs_segfault::main () at src/main.rs:20
@inseo-oh inseo-oh changed the title Calling Glyph::to_bitmap invalidates the Glyph and causes segfault Dropping BitmapGlyph created by Glyph::to_bitmap invalidates the Glyph and causes segfault Dec 3, 2020
@inseo-oh
Copy link
Author

inseo-oh commented Dec 3, 2020

A bit unrelated, but I also noticed same issue in other methods of Glyph, such as stroke and stroke_border. They also treat self.raw as if it was new object.

@bvssvni
Copy link
Member

bvssvni commented Dec 3, 2020

Do you have ideas of how to fix this problem?

One option I can think of, is to wrap self.raw in a reference counted object, e.g. Rc<T>, to prevent Drop from freeing the object when self.raw is shared.

Another option is to use ffi::FT_Glyph_Copy in to_bitmap as in Clone:

ffi::FT_Glyph_Copy(self.raw, &mut target)

@inseo-oh
Copy link
Author

inseo-oh commented Dec 4, 2020

Do you have ideas of how to fix this problem?

One option I can think of, is to wrap self.raw in a reference counted object, e.g. Rc<T>, to prevent Drop from freeing the object when self.raw is shared.

Another option is to use ffi::FT_Glyph_Copy in to_bitmap as in Clone:

ffi::FT_Glyph_Copy(self.raw, &mut target)

I initially thought "Well since it seems to modify current one, copying is the best option", but then I realized that I actually misunderstood FreeType2 API.
I thought FT_Glyph_To_Bitmap(as well as stroking functions) modifies the current one, but the FT2 writes pointer to new output using the input pointer. So it does create new copy of the glyph.

After more digging and searching, I found the "real" issue.
https://stackoverflow.com/questions/20874056/draw-text-outline-with-freetype#comment82713871_28078293

It turns out FT_Glyph_To_Bitmap will NOT replace the given pointer if glyph was already rendered(because it is already bitmap glyph), and still returns error code 0(successful). So replacing this

face.load_char('A' as usize, freetype::face::LoadFlag::RENDER)
        .unwrap();

with this does stop segfault.

face.load_char('A' as usize, freetype::face::LoadFlag::DEFAULT)
        .unwrap();

Should I open new issue for this one...?

@bvssvni
Copy link
Member

bvssvni commented Dec 7, 2020

@YeonJi2 Yeah, that would be nice!

@inseo-oh
Copy link
Author

inseo-oh commented Dec 7, 2020

Closing this one, because I think new one(#234) is the real cause of the segfault. It is basically the same thing as I mentioned above, but with a little bit more detail.
(Again, I should've read FreeType2 docs more carefully...)

@inseo-oh inseo-oh closed this as completed Dec 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants