zig
zig copied to clipboard
Segmentation fault when calling function pointer directly from packed struct
Zig Version
0.13.0
Steps to Reproduce and Observed Behavior
Calling the function pointer directly from the packed struct
in the example causes a segmentation fault.
Is this an expected undefined behavior due to a misaligned pointer or the compiler should take care of it?
Example
pub const X = packed struct {
b: bool = false,
func: *const fn () void,
const Self = @This();
pub fn init(
func: *const fn () void,
) !*Self {
const s = try std.heap.page_allocator.create(Self);
s.func = func;
return s;
}
pub fn foo(self: *Self) void {
self.func();
}
};
Test:
test "func pointer in packed struct" {
const S = struct {
pub fn foo() void {
std.debug.print("Hi!\n", .{});
}
};
std.debug.print("funcOffset={d}, bOffset={d}\n", .{
@offsetOf(X, "func"),
@offsetOf(X, "b"),
});
const x = try X.init(S.foo);
x.foo();
}
Output:
funcOffset=0, bOffset=0
Segmentation fault at address 0x12229a0
C:\Users\dev\Source\Repos\repro\src\test_tmp.zig:31:18: 0x911507 in foo (test.exe.obj)
self.func();
^
C:\Users\dev\Source\Repos\repro\src\test_tmp.zig:49:10: 0x9112dd in test.Test (test.exe.obj)
x.foo();
^
C:\Users\dev\AppData\Local\Microsoft\WinGet\Packages\zig.zig_Microsoft.Winget.Source_8wekyb3d8bbwe\zig-windows-x86_64-0.13.0\lib\compiler\test_runner.zig:157:25: 0x91995d in mainTerminal (test.exe.obj)
if (test_fn.func()) |_| {
^
C:\Users\dev\AppData\Local\Microsoft\WinGet\Packages\zig.zig_Microsoft.Winget.Source_8wekyb3d8bbwe\zig-windows-x86_64-0.13.0\lib\compiler\test_runner.zig:37:28: 0x9117a8 in main (test.exe.obj)
return mainTerminal();
^
C:\Users\dev\AppData\Local\Microsoft\WinGet\Packages\zig.zig_Microsoft.Winget.Source_8wekyb3d8bbwe\zig-windows-x86_64-0.13.0\lib\std\start.zig:363:53: 0x911523 in WinStartup (test.exe.obj)
std.os.windows.ntdll.RtlExitUserProcess(callMain());
^
???:?:?: 0x7ffc4ef3257c in ??? (KERNEL32.DLL)
???:?:?: 0x7ffc4fbcaf27 in ??? (ntdll.dll)
error: the following test command failed with exit code 3:
Swapping the b
and func
fields makes the segmentation fault go away:
pub const X = packed struct {
- b: bool = false,
- func: *const fn () void,
+ func: *const fn () void,
+ b: bool = false,
const Self = @This();
pub fn init(
func: *const fn () void,
) !*Self {
const s = try std.heap.page_allocator.create(Self);
s.func = func;
return s;
}
pub fn foo(self: *Self) void {
self.func();
}
};
Output 2
funcOffset=0, bOffset=8
Hi!
All 1 tests passed.
also adding padding after
b
makes it work:b: bool = false, + _padd: std.meta.Int(.unsigned, @bitSizeOf(*const fn () void) - @bitSizeOf(bool)), func: *const fn () void,
A few other observation emerged from this short discussion on reddit:
- Replacing the function pointer with a pointer to any other value and dereferencing it directly seems to work fine.
- assigning
self.func
to a local variable and calling that instead ofself.func()
works (e.gconst a = z.func; a();
) - changing the alignment of the func pointer also works (e.g
(@as(*const fn () void, @alignCast(self.func)))();
)
Thanks!
Expected Behavior
Being able to call the function pointer directly from the packed struct.