Segmentation fault in embree when using any allocator anywhere in code

zig c++ hello_embree.cpp works fine, just as the zig version without alloc part - they both work fine and as expected, no segfaults, correct result.

it’s only when you add that alloc part in the end when embree starts to crash.

i’ve tried adding tbb - same problem,
and glfw i’m not using here and embree does not depend on it, so it should not be required

Replace the comment with these, to see which fails and which dos not fail.

const alloc: std.mem.Allocator = std.heap.c_allocator;
_ = alloc;
const alloc: std.mem.Allocator = std.heap.c_allocator;
_ = try alloc.alloc(f32, 1);
const alloc: std.mem.Allocator = std.heap.c_allocator;
const data = try alloc.alloc(f32, 1);
alloc.free(data);
const alloc: std.mem.Allocator = std.heap.c_allocator;
const data = try alloc.alloc(f32, 1);
defer alloc.free(data);

1 = ok
2 = segfault (same as before)
3 = segfault (same as before)
4 = segfault (same as before)

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
_ = try allocator.alloc(f32, 1);

interesting. This works.

but: duplicate the last line, and it segfaults again (in the same place, before reaching this code)

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
_ = try allocator.alloc(f32, 1);
_ = try allocator.alloc(f32, 1);

also the size being allocated does not seem to matter. behaviour is the same with 0, 1 and 10000

  1. Generate rtcore.zig from rtcore.h (it’s the same output with @cInclude).
zig translate-c /path/to/embree3/rtcore.h -I /usr/include > rtcore.zig
  1. Find in rtcore.zig all the definitions for the types and functions used in the example.
    e.g. How is embree.RTCIntersectContext defined? We give a pointer to an allocation in stack, is the allocated size correct?

file generated

but i’m not sure how to check for size correctness.

just in case - generated file is here: PrivateBin

and it should correspond to this blob of embree: embree/include/embree3/rtcore_common.h at 698442324ccddd11725fb8875275dc1384f7fb40 · embree/embree · GitHub

looking at RTCIntersectContext - i see some macro ifs and now i wonder, since they can change struct’s size

from what I could gather - zig created correct struct, at least in case of RTCIntersectContext since mentioned inside RTC_MAX_INSTANCE_LEVEL_COUNT should default to 1. actually, how did zig manage to generate it correctly? :slight_smile: how did it know default value should be 1 ? (ok, my bad, found all defines in rtcore_config.h)

run: lldb ./bug

in lldb:

breakpoint set -b bug.main
run

frame variable displays all the local variables
s runs the next line
print name where name is a variable name, prints the variable contents
gui enters a curses ui, s also single steps there

Single step the lines, until the program crashes.
Try to print the variables before crashing.

I am out of ideas.

Thank you for the help so far!

did that,
right before segfault variables are these:

(lldb) frame variable 
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCDeviceTy *) device = 0x00000000002fd470
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCSceneTy *) scene = 0x00000000002fe810
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCGeometryTy *) geo = 0x0000000000300210
(float *) vertex_buff = 0x0000000000300450
(unsigned int *) index_buff = 0x00000000002fe7f0
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCRayHit) rayhit = {
  ray = {
    org_x = 0
    org_y = 0
    org_z = -1
    tnear = 0
    dir_x = 0
    dir_y = 0
    dir_z = 1
    time = 0
    tfar = +Inf
    mask = 1
    id = 0
    flags = 0
  }
  hit = {
    Ng_x = 0
    Ng_y = 0
    Ng_z = 0
    u = 0
    v = 0
    primID = 0
    geomID = 4294967295
    instID = ([0] = 0)
  }
}
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCIntersectContext) inter_context = {
  flags = 0
  filter = 0x0000000000000000
  instID = ([0] = 4294967295)
}
(lldb) 

next s results in crash:

(lldb) s
Process 549 stopped
* thread #1, name = 'zigembree', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
    frame #0: 0x00007ffff720c098 libembree3.so.3`embree::avx2::BVHNIntersector1<4, 1, false, embree::avx2::ArrayIntersector1<embree::avx2::TriangleMIntersector1Moeller<4, true>>>::intersect(embree::Accel::Intersectors const*, embree::RayHitK<1>&, embree::IntersectContext*) + 72
libembree3.so.3`embree::avx2::BVHNIntersector1<4, 1, false, embree::avx2::ArrayIntersector1<embree::avx2::TriangleMIntersector1Moeller<4, true>>>::intersect:
->  0x7ffff720c098 <+72>: vmovaps 0x10(%rsi), %xmm5
    0x7ffff720c09d <+77>: vmovss 0x20(%rsi), %xmm10        ; xmm10 = mem[0],zero,zero,zero 
    0x7ffff720c0a2 <+82>: movl   $0x0, 0x228(%rsp)
    0x7ffff720c0ad <+93>: leaq   0x220(%rsp), %r11

and this is frame variable of the working binary from the same point, everything seem to be the same, except addresses

(lldb) frame variable 
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCDeviceTy *) device = 0x00000000002ec470
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCSceneTy *) scene = 0x00000000002ed810
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCGeometryTy *) geo = 0x00000000002ef210
(float *) vertex_buff = 0x00000000002ef450
(unsigned int *) index_buff = 0x00000000002ed7f0
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCRayHit) rayhit = {
  ray = {
    org_x = 0
    org_y = 0
    org_z = -1
    tnear = 0
    dir_x = 0
    dir_y = 0
    dir_z = 1
    time = 0
    tfar = +Inf
    mask = 1
    id = 0
    flags = 0
  }
  hit = {
    Ng_x = 0
    Ng_y = 0
    Ng_z = 0
    u = 0
    v = 0
    primID = 0
    geomID = 4294967295
    instID = ([0] = 0)
  }
}
(.home.user.projects.zigembree.zig-cache.o.17a1433213ec067c54214c531e7f664f.cimport.struct_RTCIntersectContext) inter_context = {
  flags = 0
  filter = 0x0000000000000000
  instID = ([0] = 4294967295)
}

Try to build for a generic target like: -target x86_64-linux-gnu or -Dtarget=x86_64-linux-gnu.

might be a 0.11.0 compiler bug

1 Like

unfortunately, result is the same

Could be a thing where it assumes everything is 16-byte-aligned. Will get back to this tomorrow.

2 Likes

is there anything I could try or check on my own?

Keep track of the various addresses, when debugging the code that does not crash vs the code that crash. (e.g. The address of scene minus the address of device remains the same?)
Write down all the stack variables addresses for the crash and not crash case (to print the address of a variable use: p &variable)
Check if the variables are aligned, the library expects the alignment to be 16 bytes, that means that the last hex digit in address must be 0.
Maybe when the stack size is increasing, by adding more variables and/or calls that return values, something is changing in the placement of the other variables.
Note the differences, see also what happens when you remove the @alignCast.
EDIT: see what happens if you place align(16) after * or ] in variable declarations.
e.g.

var vertex_buff: [*]align(16) f32 = ...
1 Like

oh my!

i’ve checked that vertex_buff and index_buff are 16-aligned, and difference between them stays the same,
BUT
address of rayhit variable on the stack was 16-aligned when program run, and not aligned when it crashed.
so changing it’s declaration to

var rayhit: embree.RTCRayHit align(16) = std.mem.zeroes(embree.RTCRayHit);

prevented the crash!
Have not tested anything else, but at least all cases above are not segfaulting any more

Is this a valid thing i did there?

1 Like

Yes, it is valid.

What is happening:
There is a define macro RTC_ALIGN(x) called with x=16.
This is translated by the c preprocessor to __attribute__((aligned(16))) that means: the alignment must be at least 16 bytes.
Unfortunately zig translate-c does not work correctly and ignores the alignment directive.

Workaround:
If you have RTC_ALIGN(16) in the C declaration you put align(16) in zig variable type.

1 Like

Understood! nice to have a workaround, thank you!

Is it possible to define a type that is same as one coming from embree header, but with alignment?

something like

const RTCRayHit = embree.RTCRayHit align(16);

(the way above does not work obviously)

Currently it does not work for types; it works for variable declaration.
Discussion: Difference between struct level align(x) and field level align(x)

2 Likes