-1

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
Constantin
  • 8,721
  • 13
  • 75
  • 126
  • 1
    *copy_nonoverlapping* — are your source and destination memory addresses non-overlapping? What if you use `copy` instead? – Shepmaster Apr 06 '22 at 18:10
  • I tried using `std::ptr::copy` with the same result. Source & destination address should not overlap - I double-checked outputting the addresses using `println!("{:?}, {:?}", vertices.as_ptr() as *const u8, tvb.data as *mut u8);` and they are apart enough i.e. `src: 0x7fff4de4a008, dst: 0x7f367efff040 ` – Constantin Apr 07 '22 at 09:53

1 Answers1

1

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]>(),
        );
    }
}
Locke
  • 7,626
  • 2
  • 21
  • 41
  • Thank you - it makes a lot of sense! I tried the same, but unfortunately still the same SIGSEGV did occurred (updated my question with the details) – Constantin Apr 07 '22 at 09:55
  • Actually you were correct! There was a different issue in unrelated code as well causing an second SIGSEGV even after this change. Rectifying this did solve the problem - thank you very much! – Constantin Apr 07 '22 at 16:48