neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 4f44705a479d7194ec1a1c0efec7bd31a91acbb2
parent 992eaac08218c037ffa8980f36a628f5b293355b
Author: bfredl <bjorn.linse@gmail.com>
Date:   Fri, 16 Jan 2026 09:14:54 +0100

Merge pull request #37189 from bfredl/zigsafe

ci: test -Doptimize=ReleaseSafe
Diffstat:
M.github/workflows/test.yml | 10++++++----
Mbuild.zig | 44+++++++++++++++++++++++---------------------
Msrc/nvim/eval/typval.c | 4+++-
Mtest/unit/eval/testutil.lua | 4++--
4 files changed, 34 insertions(+), 28 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml @@ -209,6 +209,8 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 45 name: build using zig build (linux) + env: + OPTS: -Doptimize=ReleaseSafe steps: - uses: actions/checkout@v6 - uses: mlugg/setup-zig@v2 @@ -216,10 +218,10 @@ jobs: version: 0.15.2 - run: sudo apt-get install -y inotify-tools - - run: zig build test_nlua0 - - run: zig build nvim_bin && ./zig-out/bin/nvim --version - - run: zig build unittest - - run: zig build functionaltest + - run: zig build $OPTS test_nlua0 + - run: zig build $OPTS nvim_bin && ./zig-out/bin/nvim --version + - run: zig build $OPTS unittest + - run: zig build $OPTS functionaltest # `zig build` uses a lua script for doctags in order to support cross-compiling # compare with the builtin generator that they match - run: cd runtime; ../zig-out/bin/nvim -u NONE -i NONE -e --headless -c "helptags ++t doc" -c quit diff --git a/build.zig b/build.zig @@ -141,10 +141,12 @@ pub fn build(b: *std.Build) !void { .target = target, .optimize = optimize, }) else null; + // TODO(bfredl): fix upstream bugs with UBSAN + const optimize_ts = .ReleaseFast; const treesitter = if (system_integration_options.tree_sitter) null else b.lazyDependency("treesitter", .{ .target = target, - .optimize = .ReleaseFast, + .optimize = optimize_ts, }); const nlua0 = try build_lua.build_nlua0( @@ -623,31 +625,31 @@ pub fn build(b: *std.Build) !void { xxd_exe.linkLibC(); test_deps.dependOn(&b.addInstallArtifact(xxd_exe, .{}).step); - const parser_c = b.dependency("treesitter_c", .{ .target = target, .optimize = optimize }); - test_deps.dependOn(add_ts_parser(b, "c", parser_c.path("."), false, target, optimize, .test_)); - install.dependOn(add_ts_parser(b, "c", parser_c.path("."), false, target, optimize, .install)); + const parser_c = b.dependency("treesitter_c", .{ .target = target, .optimize = optimize_ts }); + test_deps.dependOn(add_ts_parser(b, "c", parser_c.path("."), false, target, optimize_ts, .test_)); + install.dependOn(add_ts_parser(b, "c", parser_c.path("."), false, target, optimize_ts, .install)); - const parser_markdown = b.dependency("treesitter_markdown", .{ .target = target, .optimize = optimize }); - test_deps.dependOn(add_ts_parser(b, "markdown", parser_markdown.path("tree-sitter-markdown/"), true, target, optimize, .test_)); - install.dependOn(add_ts_parser(b, "markdown", parser_markdown.path("tree-sitter-markdown/"), true, target, optimize, .install)); - test_deps.dependOn(add_ts_parser(b, "markdown_inline", parser_markdown.path("tree-sitter-markdown-inline/"), true, target, optimize, .test_)); - install.dependOn(add_ts_parser(b, "markdown_inline", parser_markdown.path("tree-sitter-markdown-inline/"), true, target, optimize, .install)); + const parser_markdown = b.dependency("treesitter_markdown", .{ .target = target, .optimize = optimize_ts }); + test_deps.dependOn(add_ts_parser(b, "markdown", parser_markdown.path("tree-sitter-markdown/"), true, target, optimize_ts, .test_)); + install.dependOn(add_ts_parser(b, "markdown", parser_markdown.path("tree-sitter-markdown/"), true, target, optimize_ts, .install)); + test_deps.dependOn(add_ts_parser(b, "markdown_inline", parser_markdown.path("tree-sitter-markdown-inline/"), true, target, optimize_ts, .test_)); + install.dependOn(add_ts_parser(b, "markdown_inline", parser_markdown.path("tree-sitter-markdown-inline/"), true, target, optimize_ts, .install)); - const parser_vim = b.dependency("treesitter_vim", .{ .target = target, .optimize = optimize }); - test_deps.dependOn(add_ts_parser(b, "vim", parser_vim.path("."), true, target, optimize, .test_)); - install.dependOn(add_ts_parser(b, "vim", parser_vim.path("."), true, target, optimize, .install)); + const parser_vim = b.dependency("treesitter_vim", .{ .target = target, .optimize = optimize_ts }); + test_deps.dependOn(add_ts_parser(b, "vim", parser_vim.path("."), true, target, optimize_ts, .test_)); + install.dependOn(add_ts_parser(b, "vim", parser_vim.path("."), true, target, optimize_ts, .install)); - const parser_vimdoc = b.dependency("treesitter_vimdoc", .{ .target = target, .optimize = optimize }); - test_deps.dependOn(add_ts_parser(b, "vimdoc", parser_vimdoc.path("."), false, target, optimize, .test_)); - install.dependOn(add_ts_parser(b, "vimdoc", parser_vimdoc.path("."), false, target, optimize, .install)); + const parser_vimdoc = b.dependency("treesitter_vimdoc", .{ .target = target, .optimize = optimize_ts }); + test_deps.dependOn(add_ts_parser(b, "vimdoc", parser_vimdoc.path("."), false, target, optimize_ts, .test_)); + install.dependOn(add_ts_parser(b, "vimdoc", parser_vimdoc.path("."), false, target, optimize_ts, .install)); - const parser_lua = b.dependency("treesitter_lua", .{ .target = target, .optimize = optimize }); - test_deps.dependOn(add_ts_parser(b, "lua", parser_lua.path("."), true, target, optimize, .test_)); - install.dependOn(add_ts_parser(b, "lua", parser_lua.path("."), true, target, optimize, .install)); + const parser_lua = b.dependency("treesitter_lua", .{ .target = target, .optimize = optimize_ts }); + test_deps.dependOn(add_ts_parser(b, "lua", parser_lua.path("."), true, target, optimize_ts, .test_)); + install.dependOn(add_ts_parser(b, "lua", parser_lua.path("."), true, target, optimize_ts, .install)); - const parser_query = b.dependency("treesitter_query", .{ .target = target, .optimize = optimize }); - test_deps.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize, .test_)); - install.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize, .install)); + const parser_query = b.dependency("treesitter_query", .{ .target = target, .optimize = optimize_ts }); + test_deps.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize_ts, .test_)); + install.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize_ts, .install)); var unit_headers: ?[]const LazyPath = null; if (support_unittests) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c @@ -2029,7 +2029,9 @@ dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - dictitem_T *const di = xmalloc(offsetof(dictitem_T, di_key) + key_len + 1); + // Allocating a struct smaller than its static size is UB (#37160) + dictitem_T *const di = xmalloc(MAX(sizeof(dictitem_T), + offsetof(dictitem_T, di_key) + key_len + 1)); memcpy(di->di_key, key, key_len); di->di_key[key_len] = NUL; di->di_flags = DI_FLAGS_ALLOC; diff --git a/test/unit/eval/testutil.lua b/test/unit/eval/testutil.lua @@ -413,7 +413,7 @@ local function alloc_len(len, get_ptr) if type(len) == 'string' or type(len) == 'table' then return #len elseif len == nil then - return eval.strlen(get_ptr()) + return tonumber(eval.strlen(get_ptr())) else return len end @@ -435,7 +435,7 @@ local alloc_logging_t = { end) return { func = 'malloc', - args = { ffi.offsetof('dictitem_T', 'di_key') + size + 1 }, + args = { math.max(ffi.sizeof('dictitem_T'), ffi.offsetof('dictitem_T', 'di_key') + size + 1) }, ret = void(di), } end,