Skip to content

Segfault in OutboundPort::notify during evalExprWithProgress (Signal 11, libLLVM) #832

Description

@lightx

Describe the bug

nixd segfaults (Signal 11/SEGV) during expression evaluation while sending a WorkDoneProgressEnd LSP notification. The crash occurs in llvm::json::isUTF8(llvm::StringRef) called from lspserver::OutboundPort::notify() — the StringRef appears to reference invalid memory (address 0x59d44836c755 is clearly not a valid pointer), causing a null-pointer-adjacent dereference inside libLLVM.so.21.1.

Stack trace (crashing thread 1671726)

#0  0x000078e2986463d9 llvm::json::isUTF8(llvm::StringRef, unsigned long*) (libLLVM.so.21.1 + 0xc463d9)
#1  0x000078e2a1c833c0 lspserver::OutboundPort::notify(llvm::StringRef, llvm::json::Value) (libnixd-lspserver.so + 0x213c0)
#2  0x000078e2a1de21c8 llvm::detail::UniqueFunctionBase<...>::CallImpl<lspserver::LSPServer::mkOutNotifiction<WorkDoneProgressEnd>...> (libnixd.so + 0x1141c8)
#3  0x000078e2a1db20f2 llvm::detail::UniqueFunctionBase<...>::CallImpl<nixd::Controller::evalExprWithProgress(...)...> (libnixd.so + 0xe40f2)
#4  0x000078e2a1de6dcf llvm::detail::UniqueFunctionBase<...>::CallImpl<lspserver::LSPServer::mkOutMethod<...>...> (libnixd.so + 0x118dcf)
#5  0x000078e2a1c8c19d lspserver::LSPServer::onReply(llvm::json::Value, llvm::Expected<llvm::json::Value>) (libnixd-lspserver.so + 0x2a19d)
#6  0x000078e2a1c8320b lspserver::InboundPort::dispatch(llvm::json::Value, lspserver::MessageHandler&) (libnixd-lspserver.so + 0x2120b)
#7  0x000078e2a1c851a9 lspserver::InboundPort::loop(lspserver::MessageHandler&) (libnixd-lspserver.so + 0x231a9)

The main thread (1671677) was in Controller::~Controller()_M_erase (destroying AttrSetClientProc map entries), waiting on thread join — indicating nixd was shutting down while the eval thread was still trying to send progress notifications through a dying/dead channel.

Kernel log

May 24 17:39:41 hornet kernel: nixd[1671726]: segfault at 59d44836c755 ip 000078e2986463d9 sp 000078e2799ce340 error 4 in libLLVM.so.21.1[c463d9,78e2983b0000+5bd0000] likely on CPU 10 (core 2, socket 0)
May 24 17:39:41 hornet kernel: Code: 25 28 00 00 00 48 89 45 f8 31 c0 48 39 fe 74 41 48 89 f9 48 89 f8 eb 12 66 0f 1f 84 00 00 00 00 00 48 83 c0 01 48 39 c6 74 27 <80> 38 00 79 f2 48 8d 7d f0 48 89 55 e0 48 89 4d f0 48 89 4d e8 e8

The <80> 38 00 instruction is cmp byte [rax], 0 — classic strlen-style scan hitting an invalid pointer.

Configuration

{
  "nixd": {
    "formatting": { "command": ["nixfmt"] }
  }
}

Running inside Neovim via nvim-lspconfig.

To Reproduce

Not reliably reproducible — this is an intermittent crash that occurs during heavy flake evaluation. The flake in question (nix flake check) passes cleanly. This appears to be a race condition where the progress notification callback runs after or during controller teardown.

Steps that may trigger it:

  1. Open a large NixOS flake in an editor with nixd LSP
  2. Wait for nixd to begin evaluating (progress notifications fire)
  3. Crash occurs when the LSP channel disconnects or the controller starts shutting down while eval callbacks are still pending

Expected behavior

nixd should not segfault. Progress notifications should be safely dropped if the outbound channel is no longer valid.

Additional context

  • nixd version: 2.9.1
  • NixOS 25.05 (Warbler), Linux 6.18.26
  • x86_64
  • Nix store path: /nix/store/nq6rgzv6kxs01jncg3b19fdygfnxkjwh-nixd-2.9.1
  • No custom nixd config beyond formatting command
  • Flakes evaluation with home-manager integration (7 hosts)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions