Skip to content

Instantly share code, notes, and snippets.

@eamonburns
Last active December 31, 2025 18:25
Show Gist options
  • Select an option

  • Save eamonburns/abcdc76fc9f97994939b87d550937f45 to your computer and use it in GitHub Desktop.

Select an option

Save eamonburns/abcdc76fc9f97994939b87d550937f45 to your computer and use it in GitHub Desktop.
Minimal reproduction for a UBSan error in ReleaseSafe but not Debug optimize mode
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const disable_check = b.option(bool, "disable_check", "Add `-fno-sanitize=object-size` to C flags") orelse false;
const exe = b.addExecutable(.{
.name = "ubsan_fail",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
const flags: []const []const u8 = if (disable_check) &.{"-fno-sanitize=object-size"} else &.{};
exe.linkLibC();
exe.addCSourceFiles(.{ .files = &.{"main.c"}, .flags = flags });
b.installArtifact(exe);
}
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
double x;
char y;
char fam[];
} FlexArray;
void create_struct(char *key, size_t key_len)
{
size_t offset = offsetof(FlexArray, fam);
size_t alignment = alignof(FlexArray);
size_t aligned_size = (offset + key_len + 1 + alignment - 1) / alignment * alignment;
FlexArray *m = malloc(offset + key_len + 1);
printf("offset=%zu, (key_len + 1)=%zu, allocated=%zu, size=%zu, alignment=%zu, aligned_size=%zu\n",
offset, key_len + 1, offset + key_len + 1, sizeof(FlexArray), alignment, aligned_size);
memcpy(m->fam, key, key_len);
m->fam[key_len] = 0;
printf(" (success)\n");
}
int main(void)
{
char *key = "howdy, partner!";
size_t offset = offsetof(FlexArray, fam);
printf("First let's loop over key sizes the compiler can't reason about:\n");
printf("======================================================================\n");
for (size_t key_len = 0; key_len < sizeof(FlexArray) - offset + 2;
key_len++) {
create_struct(key, key_len);
}
printf("\nNow let's try with a statically known size EQUAL TO the total size:\n");
printf("======================================================================\n");
create_struct(key, sizeof(FlexArray) - offset - 1);
printf("\nNow let's try with a statically known size LESS THAN the total size:\n");
printf("======================================================================\n");
create_struct(key, sizeof(FlexArray) - offset - 2);
return 0;
}
@eamonburns
Copy link
Author

eamonburns commented Dec 31, 2025

This is a minimal reproduction of an issue I had when building NeoVim with Zig (issue: neovim/neovim#37160).

The repro crashes in ReleaseSafe mode due to an illegal instruction (inserted by UBSan), but not in Debug mode.

(credit goes to @kylesower for creating the repro)

Commands

# succeeds
zig build -Doptimize=Debug && ./zig-out/bin/ubsan_fail
# fails
zig build -Doptimize=ReleaseSafe && ./zig-out/bin/ubsan_fail # --> 'Illegal instruction        (core dumped) ./zig-out/bin/ubsan_fail'
# succeeds
zig build -Doptimize=ReleaseFast && ./zig-out/bin/ubsan_fail
# succeeds
zig build -Doptimize=ReleaseSmall && ./zig-out/bin/ubsan_fail

You can use the -Ddisable_check option to disable the specific check that is failing.

# succeeds
zig build -Ddisable_check -Doptimize=ReleaseSafe && ./zig-out/bin/ubsan_fail

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment