Table of Contents

1) Introduction

nvidia-modprobe is a setuid-root helper utility for the proprietary Nvidia GPU display driver that loads kernel modules and creates character devices required for userspace GPU access. Normally, drivers do this via udev. However, kernel licensing restrictions prohibit Nvidia’s proprietary kernel module from generating uevents, which are required for udev to work. Therefore this special helper is needed.

We reviewed nvidia-modprobe as part of our whitelisting process, which requires an audit for all newly introduced setuid binaries in openSUSE. The version we reviewed was 550.127.05 and this report is based on that version. Upstream released a bugfix in version 550.144.03 and a security advisory.

2) wait3() as a Side Channel in Setuid Programs

The wait3() system call allows the calling process to obtain status information for child processes, similar to waitpid(). Unlike waitpid(), wait3() also returns resource usage information. The measurements returned by this call include CPU time, memory consumption and lower-level information such as the number of minor and major page faults that occurred during the child’s runtime. See also man 2 getrusage.

Perhaps surprisingly, wait3() also works for setuid sub-processes, leaking quite a bit of information about the behavior of the target program, which is running with elevated privileges.

A convenient way to try this out is GNU Time, a small utility that spawns a target process and prints the output of wait3(), for example:

/usr/bin/time -v nvidia-modprobe

3) File Existence Test (CVE-2024-0149)

In the case of nvidia-modprobe, we can leverage wait3() for a file existence test.

When executed with the option -f NVIDIA-CAPABILITY-DEVICE-FILE (an arbitrary path), nvidia-modprobe performs the following steps:

  • attempt to open the supplied path as root
    • if the path does exist:
      • read one or more lines
      • parse each line (implemented safely)
      • exit silently, return code 0
    • if the path does not exist:
      • exit silently, return code 0

It turns out that reading the first line of the supplied path sometimes causes a minor page fault. The number of page faults is not perfectly constant across multiple executions, depending on whether the page mapped by the kernel is dirty or not. However, if the file does not exist, it cannot be read, and therefore no page faults will be triggered. We can execute nvidia-modprobe repeatedly, calculate the median number of page faults, and infer whether the supplied path exists or not, even if the caller does not have the necessary file system permissions.

Simplified example:

$ /usr/bin/time -q --format=%R nvidia-modprobe -f /root/.bash_history
80

$ /usr/bin/time -q --format=%R nvidia-modprobe -f /root/does/not/exist
79

The output fluctuates, but it only takes a few repetitions to get a reliable signal from the median.

4) Bugfix

Upstream published a bugfix. This commit limits the queried path to files below /proc/driver/nvidia before attempting to read from it, eliminating the information leak.

5) CVE Assignment

Upstream assigned CVE-2024-0149 for this issue.

6) Other Packages

Considering the relatively obscure nature of this side channel attack, we decided to briefly look into a couple of other packages exhibiting similar usage patterns:

  • shadow
    • chsh: negative
  • util-linux
    • mount -T: negative
    • umount: negative
  • v4l-linux: positive, but does not require wait3(), and the issue was already known (CVE-2020-1369).

Even though we did not find additional instances of this problem, and the severity of this vulnerability is rather low, it’s still one of many pitfalls to keep in mind when writing or auditing setuid programs.

7) Timeline

2024-10-02 We noticed the issue and started tracking it privately in bsc#1231257.
2024-10-09 We shared the information with NVIDIA PSIRT via email, offering coordinated disclosure.
2024-10-12 We received an initial confirmation from Nvidia.
2024-10-22 After a fruitful discussion, mostly regarding tangential questions, we agreed on 2025-01-16 as the Coordinated Release Date.
2025-01-16 CVE-2024-0149 was assigned by Nvidia.
2025-01-16 Nvidia released the fix as part of version 550.144.03

8) References