(Full disclosure - this issue was investigated and report created with help from Claude Code, with significant direction from me)
Nsight Graphics 2026.2: 100% SIGSEGV intercepting GLFW OpenGL context creation
Summary
Nsight Graphics 2026.2.0.0 crashes the target process 100% of the time when
it intercepts OpenGL/GLX context creation performed by GLFW. The fault is a race
in Nsight’s GL interception layer (libNvda.Graphics.Interception.so): during
glfwCreateWindow, libNvda’s make-context-current hook probes the driver with
glGetString(GL_VENDOR) through a per-context dispatch-table slot that has not
yet been populated, and jumps through the uninitialized slot.
A ~30-line pure-GLFW program reproduces it deterministically. Inserting a
debugger breakpoint at the faulting instruction drops the crash rate to 0%,
which pins it as a race. A Qt/QOpenGLWidget application on the same machine,
under the same Nsight build, does not crash — so this is specific to GLFW’s
context-creation path, not OpenGL-under-Nsight in general.
Environment
| Component | Version |
|---|---|
| Nsight Graphics | 2026.2.0.0 (nsight-graphics-for-linux-2026.2.0.0) |
| NVIDIA driver | 580.159.03 |
| GPU | NVIDIA RTX A1000 6GB Laptop GPU |
| OS | Ubuntu 24.04.4 LTS, kernel 6.8.0-110-generic, X11 |
| GLFW | 3.3.10 (distro libglfw.so.3) |
| GL context (clean) | 4.6.0 NVIDIA 580.159.03 |
Reproduced under both injection paths:
- Nsight’s normal Launch (ptrace) attach, and
LD_PRELOAD=…/libNomad.Injection.so(used below — most convenient for a
standalone report). The symmetry across ptrace and LD_PRELOAD shows the fault
is in the interception logic, not the injection method.
Minimal reproducer
Links only GLFW. No GL loader (GLAD/GLEW), no application GL call, no Nsight SDK
usage. The crash happens entirely inside glfwCreateWindow.
#include <GLFW/glfw3.h>
#include <cstdio>
int main()
{
if (!glfwInit()) { std::fprintf(stderr, "glfwInit failed\n"); return 1; }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* w = glfwCreateWindow(800, 600, "repro", nullptr, nullptr); // CRASH
if (!w) { std::fprintf(stderr, "glfwCreateWindow failed\n"); glfwTerminate(); return 1; }
std::fprintf(stderr, "context created — survived\n");
glfwDestroyWindow(w);
glfwTerminate();
return 0;
}
Build and run:
g++ repro.cpp -o repro $(pkg-config --cflags --libs glfw3)
# Clean: prints "context created — survived", exit 0.
./repro
# Under Nsight interception: Segmentation fault, 100% of runs.
LD_PRELOAD=/opt/nvidia/nsight-graphics-for-linux/nsight-graphics-for-linux-2026.2.0.0/target/linux-desktop-nomad-x64/libNomad.Injection.so \
./repro
Measured: 30/30 crashes under interception, 0 without.
Crash details
Backtrace (gdb)
#0 0x0000000000000000 <jump through uninitialized slot>
#1 … libNvda.Graphics.Interception.so + 0x2f30a27 ("get GL string" wrapper)
#2 … libNvda.Graphics.Interception.so + 0x37a0511 (new-context GL-state init)
#3 … libNvda.Graphics.Interception.so + 0x3796700 (Context::makeContextCurrent hook)
#4 … libglfw.so.3 + 0x46e93 (_glfwPlatformMakeContextCurrent)
#5 … glfwCreateWindow
#6 … main
GLFW, inside glfwCreateWindow, makes its platform context current; libNvda’s
make-current hook runs and, while initialising GL state for the new context,
probes the driver vendor.
Faulting instruction and registers
0x…2f30a21: call *0x960(%rbp) ; this->slot_0x960(GLenum)
0x…2f30a27: mov %rax,%rbx ; (return address = frame #1)
rdi = rsi = r12 = 0x1f00 ; GL_VENDOR
rax = 0
%rbp is a heap-allocated libNvda dispatch object for the new context. The slot
at offset 0x960 should hold a trampoline to the driver’s glGetString. At the
moment of the call it is unpopulated, so the call jumps to garbage and faults on
instruction fetch (kernel error 14 = instruction fetch, user mode, page not
present).
It is a race (the decisive evidence)
| Setup | Crash rate |
|---|---|
| Free run | 30/30 |
Same, with a gdb breakpoint at the call *0x960(%rbp) instruction |
0/30 |
With the breakpoint, when gdb pauses at the call, the slot already reads a
valid trampoline and stepi executes cleanly. In a free run the slot is
unpopulated at call time; by the time gdb catches the SIGSEGV and pauses, the
slot has already been overwritten with the valid trampoline. So the slot value
at call time ≠ its value microseconds later. The breakpoint delay is enough for
the writer to finish first.
The uninitialized slot contents vary run to run (observed 0x0 and the
recurring 0x09691974), consistent with reading memory before the writer
populates it — not a fixed sentinel.
What does and doesn’t change it
- GL profile/version: no effect. 3.3 core, 3.3 compat, 4.5 core, 4.6 core,
3.3 forward-compatible, and the default (no hints) context all crash 100% at
the identical instruction. The vendor probe runs for every context. - No application-side workaround. Pre-create
glfwPollEvents(), invisible
window, pre-warming proc addresses, and pre-create sleeps up to 500 ms all
crash 100%. The race window is insideglfwCreateWindow’s make-current →
vendor-probe chain (one call), and the probe dispatches through a private
cached slot, so application or preload code cannot get between them. - Qt/
QOpenGLWidgetdoes not crash on the same machine/Nsight build —
attach and frame capture both succeed. The difference is GLFW’s inline
make-current->vendor-probe sequence duringglfwCreateWindow.
Suspected root cause / suggested fix
libNvda’s per-context GL dispatch table is populated lazily/asynchronously, and
the make-context-current hook’s driver-vendor probe (glGetString(GL_VENDOR))
can execute before the slot it dispatches through is written. The fix is to
ensure the dispatch slot is populated before it is first called — populate
synchronously during context setup, or have the probe go through a path that
forces resolution, rather than racing a concurrent writer.
This report should be triageable without target-application source: the
reproducer is trivial and the faulting instruction, register state, and
free-run-vs-breakpoint contrast localize the defect to libNvda’s dispatch-table
initialization.
Thank you!
~Keith Kyzivat
Staff Software Engineer, Professional Services
The Qt Company