From 4b791a21e84711ece30069ccc2bd629e15e6604d Mon Sep 17 00:00:00 2001 From: ZZDirty Date: Tue, 25 Aug 2026 23:03:32 +0800 Subject: [PATCH 1/2] fix(v3d): check nvtop_device_get_driver return before strcmp nvtop_device_get_driver() sets *driver to NULL and returns -ENOENT when the device has no driver bound. add_v3d_cards() ignored the return value and passed the result straight to strcmp(), which segfaults whenever a DRM device's parent has no bound driver. This is hit on every WSL2 system: /dev/dri/card0 is backed by the vgem platform device, which has no driver symlink in sysfs. Since every compiled-in backend enumerates all DRM devices and filters them by the parent's driver name, this NULL deref in the v3d backend crashes nvtop during gpuinfo_init_info_extraction(), before any interface is set up -- even on machines with a perfectly working NVIDIA or AMD GPU. Check the return value first, matching what the Intel backend already does in extract_gpuinfo_intel.c. --- src/extract_gpuinfo_v3d.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/extract_gpuinfo_v3d.c b/src/extract_gpuinfo_v3d.c index c92757c0..cad256da 100644 --- a/src/extract_gpuinfo_v3d.c +++ b/src/extract_gpuinfo_v3d.c @@ -195,8 +195,7 @@ static void add_v3d_cards(struct nvtop_device *dev, const char *devname, struct return; const char *driver; - nvtop_device_get_driver(parent, &driver); - if (strcmp(driver, "v3d")) + if (nvtop_device_get_driver(parent, &driver) < 0 || strcmp(driver, "v3d")) return; struct gpu_info_v3d *thisGPU = &gpu_infos[v3d_gpu_count++]; From 56485dc36d5b3acd0e63ad6b127896b617432a9e Mon Sep 17 00:00:00 2001 From: ZZDirty Date: Tue, 25 Aug 2026 23:03:44 +0800 Subject: [PATCH 2/2] fix(nvidia): prefer libnvidia-ml SONAME and retry when nvmlInit fails gpuinfo_nvidia_init() called dlopen("libnvidia-ml.so") first and only fell back to "libnvidia-ml.so.1" when that dlopen itself failed. Two problems with this: - The unversioned libnvidia-ml.so is a development symlink, whereas the SONAME libnvidia-ml.so.1 is the name the runtime driver installs. The SONAME is the one that should be tried first. - The fallback never triggered when dlopen succeeded but nvmlInit() failed, so a loadable-but-unusable library was fatal. Under WSL2 the two names can resolve to different files: libnvidia-ml.so.1 finds the WSL-provided library in /usr/lib/wsl/lib which talks to the host GPU, while the unversioned libnvidia-ml.so only exists in the native Linux driver packages installed inside the distro, whose nvmlInit() returns NVML_ERROR_DRIVER_NOT_LOADED because /dev/nvidiactl is absent. nvtop consequently reported "No GPU to monitor" on systems where nvidia-smi -- which dlopens the SONAME -- works fine. The README already advises not to install the native driver inside WSL2, but nvtop can simply pick the library that works instead of failing. Move the load-symbols-and-init sequence into gpuinfo_nvidia_init_with_lib() and try each candidate name in turn, so a failure at any step moves on to the next one. Also record the NVML error string and dlclose the handle when nvmlInit() fails, which previously leaked. --- src/extract_gpuinfo_nvidia.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/extract_gpuinfo_nvidia.c b/src/extract_gpuinfo_nvidia.c index 33670a60..d6d08078 100644 --- a/src/extract_gpuinfo_nvidia.c +++ b/src/extract_gpuinfo_nvidia.c @@ -310,11 +310,9 @@ __attribute__((constructor)) static void init_extract_gpuinfo_nvidia(void) { reg * function gpuinfo_nvidia_last_error_string. * */ -static bool gpuinfo_nvidia_init(void) { +static bool gpuinfo_nvidia_init_with_lib(const char *libname) { - libnvidia_ml_handle = dlopen("libnvidia-ml.so", RTLD_LAZY); - if (!libnvidia_ml_handle) - libnvidia_ml_handle = dlopen("libnvidia-ml.so.1", RTLD_LAZY); + libnvidia_ml_handle = dlopen(libname, RTLD_LAZY); if (!libnvidia_ml_handle) { local_error_string = dlerror(); return false; @@ -472,7 +470,8 @@ static bool gpuinfo_nvidia_init(void) { last_nvml_return_status = nvmlInit(); if (last_nvml_return_status != NVML_SUCCESS) { - return false; + local_error_string = nvmlErrorString(last_nvml_return_status); + goto init_error_clean_exit; } local_error_string = NULL; @@ -484,6 +483,26 @@ static bool gpuinfo_nvidia_init(void) { return false; } +/* + * + * Try the NVML libraries in turn. The SONAME (libnvidia-ml.so.1) comes first + * because it is the name the runtime driver always installs, while the + * unversioned libnvidia-ml.so is a development symlink that may point at a + * library unusable on this system (e.g. the native driver package installed + * inside WSL, where only the WSL-provided libnvidia-ml.so.1 can talk to the + * host GPU). + * + */ +static bool gpuinfo_nvidia_init(void) { + static const char *const nvml_libs[] = {"libnvidia-ml.so.1", "libnvidia-ml.so"}; + + for (size_t i = 0; i < sizeof(nvml_libs) / sizeof(*nvml_libs); ++i) { + if (gpuinfo_nvidia_init_with_lib(nvml_libs[i])) + return true; + } + return false; +} + static void gpuinfo_nvidia_shutdown(void) { if (libnvidia_ml_handle) { nvmlShutdown();