Search code examples
rustglfwunsafebgfx

Copying data to transient vertex buffer in bgfx causing SIGSEGV


I'm trying to port an bgfx application I wrote in C++ to Rust using bgfx-rs. Everything works great, until I try to copy some vertex data from the application to an transient vertex buffer.

let vertices: [f32; 16] = [
    0.0f32,    0.0f32,    0.0f32,    0.0f32,
    1024.0f32, 0.0f32,    1024.0f32, 0.0f32,
    0.0f32,    1024.0f32, 0.0f32,    1024.0f32,
    1024.0f32, 1024.0f32, 1024.0f32, 1024.0f32,
];
let builder = bgfx::VertexLayoutBuilder::new();
builder.begin(RendererType::Noop);
builder.add(
    bgfx::Attrib::Position,
    2,
    bgfx::AttribType::Float,
    bgfx_rs::static_lib::AddArgs {
        normalized: false,
        as_int: false,
    },
);
builder.add(
    bgfx::Attrib::TexCoord0,
    2,
    bgfx::AttribType::Float,
    bgfx_rs::static_lib::AddArgs {
        normalized: false,
        as_int: false,
    },
);
builder.end();
let mut tvb = bgfx::TransientVertexBuffer::new();
if bgfx::get_avail_transient_vertex_buffer(4, &builder) == 4 {
    bgfx::alloc_transient_vertex_buffer(&mut tvb, 4, &builder);
    unsafe {
        let mut data = *tvb.data;
        std::ptr::copy_nonoverlapping(
            vertices.as_ptr() as *const u8,
            &mut data,
            std::mem::size_of::<[f32; 16]>(),
        ); //this line causes SIGSEGV
    }
}

When I add this std::ptr::copy_nonoverlapping operation the program is crashing due to SIGSEGV signal.

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV) 

What is the best way to debug the cause of an SIGSEGV signal? I've tried running the code using GDB, but until the crash happens there is no indication of the reason visible at all (I can step through the signal handler but there is no additional information about the crash). If I copy less bytes (std::mem::size_of::<[f32; 16]>() resolves to 64 bytes; i.e. if I just copy 32 bytes instead) the application is not crashing (but obviously some of my vertex data is missing).

I'm using the unsafe block, because that is the only way I got my C++ code working fighting the borrow-checker. For reference my code in C++ (which works fine):

const float vertices[16] = {
  0.0f, 0.0f, 0.0f, 0.0f,
  1024.0f, 0.0f, 1024.0f, 0.0f,
  0.0f, 1024.0f, 0.0f, 1024.0f,
  1024.0f, 1024.0f, 1024.0f, 1024.0f
};
ms_decl // bgfx::VertexLayout
  .begin()
  .add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
  .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
  .end();
bgfx::TransientVertexBuffer tvb;
if (bgfx::getAvailTransientVertexBuffer(4, ms_decl) == 4) {
  bgfx::allocTransientVertexBuffer(&tvb, 4,ms_decl);
  bx::memCopy(tvb.data, vertices, 16 * sizeof(float));
}

The reason I have to use std::ptr::copy_nonoverlapping in the Rust version is that tvb.data is an uint8_t* in C++ (reference), while it is const u8* in Rust (reference).

Any help if there is something wrong in my unsafe block or how to get to the bottom of the SIGSEGV would be highly appreciated.

I have tried:

  • using std::ptr::copy instead (I don't expect the source & destination memory to overlap), but still the same SIGSEGV occurred

Solution

  • Assuming I am reading the correct documentation, the issue is you are copying to the stack instead of the data pointer. Since tvb.data is a *mut u8, the call let mut data = *tvb.data; dereferences the pointer and copies the value to the stack meaning the type of data is u8. Then in ptr::copy_nonoverlapping, data is referenced to get the write location. However since data is now a single byte on the stack and is not nearly large enough to hold the copied value, copy_nonoverlapping overwrites a ton of stuff on the stack leading to a segfault.

    The solution is just cast tvb.data to a mutable pointer before passing it to copy_nonoverlapping. Raw pointers can break the mutability rules a bit since they are unsafe to read either way.

    let mut tvb = bgfx::TransientVertexBuffer::new();
    if bgfx::get_avail_transient_vertex_buffer(4, &builder) == 4 {
        bgfx::alloc_transient_vertex_buffer(&mut tvb, 4, &builder);
        unsafe {
            std::ptr::copy_nonoverlapping(
                &vertices as *const u8,
                tvb.data as *mut u8,
                std::mem::size_of::<[f32; 16]>(),
            );
        }
    }