Skip to content

Drop ProtectClock= from the unit — it hides /dev/nvidia* and NVML can't init - #14

Merged
ricky-chaoju merged 1 commit into
infinirc:mainfrom
Liquescent-Development:fix/unit-protectclock-blocks-nvidia-devices
Sep 2, 2026
Merged

Drop ProtectClock= from the unit — it hides /dev/nvidia* and NVML can't init#14
ricky-chaoju merged 1 commit into
infinirc:mainfrom
Liquescent-Development:fix/unit-protectclock-blocks-nvidia-devices

Conversation

@richardkiene

Copy link
Copy Markdown
Contributor

Hi — thanks for nvfd, it's exactly the tool I was looking for after giving up on the Coolbits/X route. I hit a wall getting the service to start, though, and I think it will bite anyone on a reasonably recent systemd, so here's the fix.

What happens

systemctl start nvfd fails every time with

Failed to initialize NVML: Unknown Error

until the start limit gives up, while sudo nvfd list and sudo nvfd status from a shell work fine. Same binary, same root user, so it had to be the unit's sandboxing.

Finding it

I ran nvfd list under each hardening directive from the unit on its own with systemd-run:

for p in ProtectHome=yes ProtectSystem=strict ... ProtectClock=yes ... ; do
  sudo systemd-run --wait --pipe -p "$p" /usr/local/bin/nvfd list
done

Fourteen of the fifteen pass. ProtectClock=yes is the only one that fails. Then, with all fourteen others applied together and ProtectClock= left out, nvfd list runs and enumerates the GPU.

The reason is in systemd.exec(5): ProtectClock=yes implies DeviceAllow=char-rtc r, and adding any DeviceAllow= switches the unit to DevicePolicy=closed. From that point the service only sees the devices on the allow-list, so /dev/nvidiactl and /dev/nvidia0 aren't there and NVML can't reach the driver. The "Unknown Error" is NVML's way of saying it couldn't open the device node.

The change

Just remove ProtectClock= and leave a comment in the unit saying why it mustn't come back. The daemon never touches the clock, so nothing is lost. I did consider keeping it and adding explicit DeviceAllow= lines for the NVIDIA nodes instead, but the set of nodes depends on GPU count and driver (nvidia-uvm has a dynamic major), and a wrong guess there fails in exactly this silent way, so the simpler rule felt safer.

Tested on Ubuntu 22.04.5, systemd 249, driver 595.84, RTX 6000 Ada. Happy to adjust if you'd prefer the allow-list approach.

Under the systemd unit nvmlInit() failed with "Failed to initialize
NVML: Unknown Error" while the same binary worked from a root shell.
Bisecting the hardening directives with

  systemd-run --wait --pipe -p <directive> /usr/local/bin/nvfd list

on Ubuntu 22.04.5 / systemd 249 / driver 595.84 (RTX 6000 Ada) passed
every directive except ProtectClock=yes.

systemd.exec(5): ProtectClock=yes implies DeviceAllow=char-rtc r, and
"adding [DeviceAllow=] turns on DevicePolicy=closed by default". With a
closed device policy the daemon cannot open /dev/nvidiactl or
/dev/nvidia<N>, so NVML cannot reach the driver.

Removing ProtectClock= costs nothing the daemon needs (it never touches
the clock) and restores device access. Keeping ProtectClock= alongside
explicit DeviceAllow= entries for the NVIDIA nodes would work too, but
the set of nodes varies by GPU count and driver (nvidia-uvm has a
dynamic major), so the simpler rule is preferred.

@ricky-chaoju ricky-chaoju left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks!

@ricky-chaoju
ricky-chaoju merged commit 6c98e82 into infinirc:main Sep 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants