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

Glyph::to_bitmap creates duplicate pointer if the glyph was already rendered during Face::load_char. #234

Open
inseo-oh opened this issue Dec 7, 2020 · 0 comments

Comments

@inseo-oh
Copy link

inseo-oh commented Dec 7, 2020

If a glyph was loaded using LoadFlag::RENDER like this:

use freetype::Library;
use freetype::face::LoadFlag;
let lib = Library::init().unwrap();
let face = lib.new_face("test.ttf", 0).unwrap();

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

and created a bitmap glyph using to_bitmap:

let glyph_slot = face.glyph();
let glyph = glyph_slot.get_glyph().unwrap();
let bitmap_glyph = glyph.to_bitmap(RenderMode::Normal, None).unwrap();

In to_bitmap, not only it will pass the_glyph(which contains self's raw pointer) to FT_Glyph_To_Bitmap, but it also receives the resulting pointer through the_glyph. So the_glyph would contain pointer to the new glyph after successful FT_Glyph_To_Bitmap.

freetype-rs/src/glyph.rs

Lines 76 to 86 in 5975d3c

let mut the_glyph = self.raw;
let mut p_origin = null_mut();
if let Some(ref mut o) = origin {
p_origin = o as *mut Vector;
}
let err = unsafe {
ffi::FT_Glyph_To_Bitmap(&mut the_glyph, render_mode as u32, p_origin, 0)
};
if err == ffi::FT_Err_Ok {
Ok(unsafe { BitmapGlyph::from_raw(self.library_raw, the_glyph as ffi::FT_BitmapGlyph) })

But the problem is that FT_Glyph_To_Bitmap will NOT replace the_glyph's value if glyph was already rendered(and still return error code 0, which means it's OK). I was able to confirm this by modifying the source code like this, and checking the output:

let mut the_glyph = self.raw;
let mut p_origin = null_mut();

dbg!(the_glyph); // <- Before FT_Glyph_To_Bitmap
if let Some(ref mut o) = origin {
  p_origin = o as *mut Vector;
}
let err =
    unsafe { ffi::FT_Glyph_To_Bitmap(&mut the_glyph, render_mode as u32, p_origin, 0) };
dbg!(the_glyph); // <- After FT_Glyph_To_Bitmap
// *** Both dbg! printed the SAME result! ***
dbg!(err); // <- Just to confirm that error code was actually 0, and it was.

So to_bitmap copies the old value(which is again, self's raw pointer) to the BitmapGlyph, creating two glyphs pointing to the same glyph. Not only this means modifying old glyph can affect the "new" BitmapGlyph, but it also means when drop is called on both glyphs, it will cause double-free and eventually segfault.

Using DEFAULT flag(which does not render the glyph) does stop the segfault, and to_bitmap works without any issue.

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

So I think there should be a check if glyph is already a bitmap before calling FT_Glyph_To_Bitmap. FT_GlyphRec's format field might help that.

A code that segfaults

Below simply loads a glyph with LoadFlag::RENDER(which renders the glyph and turns it into bitmap), and then tries to convert it to bitmap. I used explicit drop call to show exactly where it was segfaulting.

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();
    // Changing RENDER to DEFAULT stops segfault.
    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");
    }
}

This will crash with following output:

    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)

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

It probably segfaulted because two glyphs were sharing the pointer(because of the bug I mentioned above), and dropping one of them invalidated the other glyph.

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

1 participant