I am porting a small codebase from Zig 0.13 to Zig 0.14, and in doing so I have encountered a problem. I am unsure whether this is an expected error related to what I am doing or maybe a bug in the new version.
I need to initialize a structure with a field whose memory is aligned. The alignment is done in the initialization function for ease of use, where the user passes an arbitrary slice as a parameter. I create an aligned buffer (on the page size, for example) and then I memcpy. I add a deinitialization function. Here is a minimal reproducible code snippet:
const std = @import("std");
const page_size = std.heap.pageSize();
const MyStruct = struct {
allocator: std.mem.Allocator,
bytes: []u8,
pub fn init(allocator: std.mem.Allocator, bytes: []const u8) !@This() {
const buffer align(page_size) = try allocator.alignedAlloc(u8, page_size, bytes.len);
@memcpy(buffer[0..bytes.len], bytes);
return .{
.allocator = allocator,
.bytes = buffer,
};
}
pub fn deinit(self: *@This()) void {
self.allocator.free(self.bytes);
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var myStruct = try MyStruct.init(allocator, &[_]u8{ 1, 2, 3, 4 });
defer myStruct.deinit();
}
This program compiles and runs correctly with Zig 0.13 (you just need to change std.heap.pageSize() to std.mem.page_size). In Zig 0.14, this program compiles correctly, but at runtime, there is an error during deinitialization:
thread 514165 panic: Invalid free
<zig-cache>/lib/std/heap/debug_allocator.zig:870:49: 0x10e2857 in free (myProgram)
if (bucket.canary != config.canary) @panic("Invalid free");
^
<zig-cache>/lib/std/mem/Allocator.zig:147:25: 0x107029b in free__anon_10944 (myProgram)
return a.vtable.free(a.ptr, memory, alignment, ret_addr);
^
<project-path>/src/main.zig:20:28: 0x10e0993 in deinit (myProgram)
self.allocator.free(self.bytes);
^
<project-path>/src/main.zig:31:26: 0x10e0854 in main (myProgram)
defer myStruct.deinit();
^
<zig-cache>/lib/std/start.zig:656:37: 0x10e01ca in posixCallMainAndExit (myProgram)
const result = root.main() catch |err| {
^
<zig-cache>/lib/std/start.zig:271:5: 0x10dfd7d in _start (myProgram)
asm volatile (switch (native_arch) {
^
???:?:?: 0x0 in ??? (???)
I found a solution to the problem. Just replace free with rawFree:
pub fn deinit(self: *@This()) void {
self.allocator.rawFree(self.bytes, std.mem.Alignment.fromByteUnits(page_size), @returnAddress());
}
By doing this, the behavior is similar to what happened in Zig 0.13, so it seems to work.
However, Iām not sure if this is the right way to do it, mainly because rawFree should not be used outside of an allocator implementation and also because I am not sure about the parameters I passed to it. This leads me to believe that it is potentially a bug, because free is what should work, right?. However, I am not very advanced in Zig yet, so I am here to ask for your opinion and information on the right way to do this. I know that in 0.14 @memcpy has changed, so I think it is possible that my implementation is not fully compliant with the changes made (but I donāt know how), hence the error.
Thanks!