> It is true that if you use unsafe Zig, i.e. turn off safety for a whole program or some sections of it, you lose the guarantees that safe Zig gives you, and unsafe Zig is indeed not safe
Their is no such things as unsafe and safe Zig. All Zig is unsafe, but you can add additional runtime checks (disabled by default in optimized builds) that will slow down your program when used. Using a specific allocator to detect UAF is something you may do in development, but almost surely never in production. And without it your code isn't memory-safe.
> Fuzzing a C program will not find all the undefined behaviour that fuzzing a Zig program can, for the reasons I mentioned.
Zig will have less UB than C, but there will still be lurking UB in your programs no matter how long you test it. Consider the following snippet (on mobile, so this may have stupid syntax errors):
test "this is UB, but the test won't show it" {
const allocator = std.heap.page_allocator;
var buf = try allocator.alloc(u8, 10);
ohNo(allocator, 42, buf);
allocator.free(bar);
}
fn ohNo(allocator: *Allocator, foo: const u8, bar: *u8) void {
if (foo == 1337) {
// double free awaiting to happen in production
allocator.free(bar);
}
}
If you never explicitly test the value “1337” during you debug session, you won't trigger the UB and you won't know it's here, then when you ship your optimized build in production, you'll ship a program with UB in it.