Package: nvtop
Version: 3.2.0-1
Severity: normal
Tags: patch

Dear nvtop maintainers!

I have identified two separate memory leaks within nvtop 3.2.0-1.
These leaks occur under specific circumstances and can lead to increased
memory usage over time.

The first leak appears to occur within the hardware monitoring (hwmon)
enumeration code. Specifically, the enumerator object is not properly
freed when errors are encountered during enumeration, resulting in a
leak.

The second leak is related to the handling of temporary object during
dynamic data updates on intel gpus. This object is created and used but not
subsequently deallocated.

I have attached the output of `valgrind --leak-check=full nvtop -s`
which demonstrates these memory leaks.

I have developed patches to address both of these issues. The patches
ensure proper cleanup of the enumerator object in the hwmon enumeration
code and correct the deallocation of temporary objects used in dynamic
data updates.

Attachments:
* `valgrind_output.txt` (Output from `valgrind --leak-check=full nvtop -s`)
* `hwmon_leak_fix.patch` (Patch for hwmon enumeration leak)
* `dynamic_data_leak_fix.patch` (Patch for dynamic data update leak)

I would greatly appreciate it if you would consider reviewing and
applying these patches to resolve these memory leak issues in future
releases of nvtop.

Thank you for your time and consideration.
==23439== Memcheck, a memory error detector
==23439== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==23439== Using Valgrind-3.24.0 and LibVEX; rerun with -h for copyright info
==23439== Command: nvtop -s
==23439== Parent PID: 27598
==23439== 
==23439== 
==23439== HEAP SUMMARY:
==23439==     in use at exit: 2,517 bytes in 14 blocks
==23439==   total heap usage: 3,792 allocs, 3,778 frees, 4,738,699 bytes 
allocated
==23439== 
==23439== 300 (144 direct, 156 indirect) bytes in 1 blocks are definitely lost 
in loss record 11 of 14
==23439==    at 0x4844818: malloc (vg_replace_malloc.c:446)
==23439==    by 0x49EAC36: sd_device_enumerator_new (in 
/usr/lib/x86_64-linux-gnu/libsystemd.so.0.40.0)
==23439==    by 0x120571: nvtop_enumerator_new (device_discovery_linux.c:251)
==23439==    by 0x120571: nvtop_device_get_hwmon (device_discovery_linux.c:342)
==23439==    by 0x124EDC: add_intel_cards (extract_gpuinfo_intel.c:111)
==23439==    by 0x124EDC: gpuinfo_intel_get_device_handles 
(extract_gpuinfo_intel.c:156)
==23439==    by 0x11BD56: gpuinfo_init_info_extraction (extract_gpuinfo.c:66)
==23439==    by 0x1110DB: main (nvtop.c:211)
==23439== 
==23439== 585 (416 direct, 169 indirect) bytes in 1 blocks are definitely lost 
in loss record 12 of 14
==23439==    at 0x4844818: malloc (vg_replace_malloc.c:446)
==23439==    by 0x49FB7E1: ??? (in 
/usr/lib/x86_64-linux-gnu/libsystemd.so.0.40.0)
==23439==    by 0x124876: gpuinfo_intel_refresh_dynamic_info 
(extract_gpuinfo_intel.c:242)
==23439==    by 0x11BE09: gpuinfo_refresh_dynamic_info (extract_gpuinfo.c:103)
==23439==    by 0x117368: print_snapshot (interface.c:2061)
==23439==    by 0x1115CF: main (nvtop.c:219)
==23439== 
==23439== LEAK SUMMARY:
==23439==    definitely lost: 560 bytes in 2 blocks
==23439==    indirectly lost: 325 bytes in 9 blocks
==23439==      possibly lost: 0 bytes in 0 blocks
==23439==    still reachable: 1,632 bytes in 3 blocks
==23439==         suppressed: 0 bytes in 0 blocks
==23439== Reachable blocks (those to which a pointer was found) are not shown.
==23439== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==23439== 
==23439== For lists of detected and suppressed errors, rerun with: -s
==23439== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
From: Alexander <>
Date: Sat, 30 May 2026 11:32:43 +0300
Subject: Free enumerator on errors

---
 src/device_discovery_linux.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/device_discovery_linux.c b/src/device_discovery_linux.c
index 1c5e5df..fdb1f76 100644
--- a/src/device_discovery_linux.c
+++ b/src/device_discovery_linux.c
@@ -339,19 +339,21 @@ int nvtop_device_current_pcie_link(nvtop_device *dev, nvtop_pcie_link *pcie_info
 
 nvtop_device *nvtop_device_get_hwmon(nvtop_device *dev) {
   nvtop_device_enumerator *enumerator;
+  nvtop_device *hwmon = NULL;
   int ret = nvtop_enumerator_new(&enumerator);
   if (ret < 0)
     return NULL;
   ret = nvtop_device_enumerator_add_match_subsystem(enumerator, "hwmon", true);
   if (ret < 0)
-    return NULL;
+    goto err;
   ret = nvtop_device_enumerator_add_match_parent(enumerator, dev);
   if (ret < 0)
-    return NULL;
-  nvtop_device *hwmon = nvtop_enumerator_get_device_first(enumerator);
+    goto err;
+  hwmon = nvtop_enumerator_get_device_first(enumerator);
   if (!hwmon)
-    return NULL;
+    goto err;
   nvtop_device_ref(hwmon);
+err:
   nvtop_enumerator_unref(enumerator);
   return hwmon;
 }
From: Alexander <>
Date: Sat, 30 May 2026 11:42:28 +0300
Subject: Free bridge_dev on intel

---
 src/extract_gpuinfo_intel.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/extract_gpuinfo_intel.c b/src/extract_gpuinfo_intel.c
index 0773713..e331a8f 100644
--- a/src/extract_gpuinfo_intel.c
+++ b/src/extract_gpuinfo_intel.c
@@ -242,6 +242,7 @@ void gpuinfo_intel_refresh_dynamic_info(struct gpu_info *_gpu_info) {
       nvtop_device_new_from_syspath(&bridge_dev_noncached, syspath);
   } else {
     bridge_dev_noncached = driver_dev_noncached;
+    nvtop_device_ref(bridge_dev_noncached);
   }
 
   nvtop_device *clock_device = gpu_info->driver == DRIVER_XE ? driver_dev_noncached : card_dev_noncached;
@@ -322,6 +323,7 @@ void gpuinfo_intel_refresh_dynamic_info(struct gpu_info *_gpu_info) {
   // Let the temporary devices be garbage collected
   nvtop_device_unref(card_dev_noncached);
   nvtop_device_unref(driver_dev_noncached);
+  nvtop_device_unref(bridge_dev_noncached);
   if (hwmon_dev_noncached)
     nvtop_device_unref(hwmon_dev_noncached);
 }

Reply via email to