** Description changed:

+ [ Impact ]
+ 
+ The Ubuntu-specific perf_event_paranoid level 4 introduces an additional
+ capability check that requires CAP_SYS_ADMIN to access perf events.
+ However, this check was implemented before CAP_PERFMON was introduced,
+ and was never updated to recognize the new capability.
+     
+ CAP_PERFMON was specifically designed to allow performance monitoring
+ operations without granting the broad privileges of CAP_SYS_ADMIN. The
+ current implementation forces users to grant CAP_SYS_ADMIN even when
+ CAP_PERFMON would be sufficient, violating the principle of least
+ privilege.
+     
+ The perfmon_capable() helper function checks for either CAP_PERFMON or
+ CAP_SYS_ADMIN, providing the intended functionality while maintaining
+ backward compatibility with systems that use CAP_SYS_ADMIN.
+     
+ This change allows processes with CAP_PERFMON to access perf events when
+ perf_event_paranoid is set to 4, while still requiring explicit grants
+ as intended by the stricter paranoid level. Processes with CAP_SYS_ADMIN
+ continue to work as before.
+ 
+ [ Test Plan ]
+ 
+ Use the following reproducer (with CAP_PERFMON file capabilities) to check
+ if CAP_SYS_ADMIN is still required.
+ 
+ ```
+ #include <stdio.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <sys/capability.h>
+ #include <sys/syscall.h>
+ #include <sys/types.h>
+ #include <linux/perf_event.h>
+ 
+ static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int 
cpu, int group_fd, unsigned long flags) {
+   return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
+ }
+ 
+ void print_capabilities(void) {
+   cap_t capabilities;
+   char *capabilities_text;
+ 
+   capabilities = cap_get_proc();
+   if (capabilities == NULL) {
+     printf("[!] Impossbile to get process capabilities.\n");
+     return;
+   }
+ 
+   capabilities_text = cap_to_text(capabilities, NULL);
+   if (capabilities_text == NULL) {
+     printf("[!] Impossbile to convert process capabilities.\n");
+     cap_free(capabilities);
+     return;
+   }
+ 
+   printf("[*] Current capabilities: %s\n", capabilities_text);
+ 
+   cap_free(capabilities_text);
+   cap_free(capabilities);
+ }
+ 
+ int check_perf_access(const char *event_name, int event_type, int 
event_config) {
+   struct perf_event_attr pe;
+   int fd;
+ 
+   memset(&pe, 0, sizeof(struct perf_event_attr));
+   pe.type = event_type;
+   pe.size = sizeof(struct perf_event_attr);
+   pe.config = event_config;
+   pe.disabled = 1;
+   pe.exclude_kernel = 0;
+   pe.exclude_hv = 1;
+ 
+   fd = perf_event_open(&pe, 0, -1, -1, 0);
+ 
+   if (fd == -1) {
+     printf("[!] %s: Impossible to open perf event\n", event_name);
+     return -1;
+   } else {
+     printf("[*] %s: Successfully opened perf event\n", event_name);
+     close(fd);
+     return 0;
+   }
+ }
+ 
+ int main(int argc, char *argv[]) {
+   int result = 0;
+   FILE *fp;
+   int paranoid_level = -2;
+ 
+   fp = fopen("/proc/sys/kernel/perf_event_paranoid", "r");
+   if (fp) {
+     if (fscanf(fp, "%d", &paranoid_level) == 1) {
+       printf("[*] Current perf_event_paranoid level: %d.\n", paranoid_level);
+       if (paranoid_level == 4) {
+         printf("[*] This is a custom Ubuntu paranoid level.\n");
+       }
+     }
+     fclose(fp);
+   } else {
+     printf("[!] Impossible to perf_event_paranoid level.\n");
+   }
+ 
+   printf("\n");
+ 
+   print_capabilities();
+ 
+   printf("\n");
+ 
+   result += check_perf_access("CPU_CYCLES", PERF_TYPE_HARDWARE, 
PERF_COUNT_HW_CPU_CYCLES);
+   result += check_perf_access("INSTRUCTIONS", PERF_TYPE_HARDWARE, 
PERF_COUNT_HW_INSTRUCTIONS);
+   result += check_perf_access("CACHE_REFERENCES", PERF_TYPE_HARDWARE, 
PERF_COUNT_HW_CACHE_REFERENCES);
+   
+   printf("\n");
+ 
+   if (result == 0) {
+     printf("All perf events accessible with current capabilities\n");
+   } else {
+     printf("Some events were not accessible with current capabilities\n");
+   }
+ }
+ ```
+ 
+ Before the patch:
+ 
+ ```
+ $ ./perf_repro 
+ [*] Current perf_event_paranoid level: 4.
+ [*] This is a custom Ubuntu paranoid level.
+ 
+ [*] Current capabilities: cap_perfmon=ep
+ 
+ [!] CPU_CYCLES: Impossible to open perf event
+ [!] INSTRUCTIONS: Impossible to open perf event
+ [!] CACHE_REFERENCES: Impossible to open perf event
+ 
+ Some events were not accessible with current capabilities
+ ```
+ 
+ After the patch:
+ ```
+ $ ./perf_repro 
+ [*] Current perf_event_paranoid level: 4.
+ [*] This is a custom Ubuntu paranoid level.
+ 
+ [*] Current capabilities: cap_perfmon=ep
+ 
+ [*] CPU_CYCLES: Successfully opened perf event
+ [*] INSTRUCTIONS: Successfully opened perf event
+ [*] CACHE_REFERENCES: Successfully opened perf event
+ 
+ All perf events accessible with current capabilities
+ ```
+ 
+ [ Regression Potential ]
+ 
+ The regression potential is minimal.
+ The fix maintains backward compatibility as perfmon_capable() accepts both
+ CAP_SYS_ADMIN and CAP_PERFMON, ensuring all curently working systems are not 
impacted by the fix.
+ No security weakening occurs since CAP_PERFMON was designed for performance 
monitoring and provides fewer privileges than CAP_SYS_ADMIN.
+ 
+ ---
+ 
  I am trying to run node_exporter without root, with cap_perfmon and 
--collector.perf as well as 
--collector.perf.hardware-profilers=CpuCycles,CpuInstr.
  Expected behavior: node_exporter exports instructions and cpu cycle metrics
  Actual behavior: no perf metrics
  
  version: Ubuntu 6.8.0-87.88-generic 6.8.12
  
  Ubuntu carries a patch that introduces a new security level for perf events: 
perf_event_paranoid=4
  This patch limits calling the perf open syscall to processes with 
CAP_SYS_ADMIN. This patch is from ~2016.
  
  Example commit for resolute: https://git.launchpad.net/~canonical-
  kernel/ubuntu/+source/linux-
  
aws/+git/resolute/commit/kernel/events/core.c?id=eaa91347f6f8112c5c567f93123bfe3b82bd1593
  
  In 2020 a new capability was introduced, CAP_PERFMON, that should be 
sufficient for using perf.
  The code now checks with perfmon_capable() if the process has CAP_SYS_ADMIN 
_or_ CAP_PERFMON. I am trying to get cpu hardware metrics with CAP_PERFMON but 
can't.
  
  Looking at the commit message, timing and effect I think the introduction of 
CAP_PERFMON was missed
  The patch
  
  > +   if (perf_paranoid_any() && !capable(CAP_SYS_ADMIN))
  > +           return -EACCES;
  
  should probably be
  
  > +   if (perf_paranoid_any() && !perfmon_capable())
  > +           return -EACCES;
  
  ProblemType: Bug
  DistroRelease: Ubuntu 24.04
  Package: linux-image-6.8.0-87-generic 6.8.0-87.88
  ProcVersionSignature: Ubuntu 6.8.0-87.88-generic 6.8.12
  Uname: Linux 6.8.0-87-generic x86_64
  ApportVersion: 2.28.1-0ubuntu3.8
  Architecture: amd64
  AudioDevicesInUse:
-  USER        PID ACCESS COMMAND
-  /dev/snd/controlC0:  rtreffer   2521 F.... wireplumber
-  /dev/snd/seq:        rtreffer   2519 F.... pipewire
+  USER        PID ACCESS COMMAND
+  /dev/snd/controlC0:  rtreffer   2521 F.... wireplumber
+  /dev/snd/seq:        rtreffer   2519 F.... pipewire
  CRDA: N/A
  CasperMD5CheckResult: pass
  Date: Mon Nov 10 21:46:59 2025
  InstallationDate: Installed on 2025-11-08 (2 days ago)
  InstallationMedia: Ubuntu-Server 24.04.3 LTS "Noble Numbat" - Release amd64 
(20250805.1)
  IwConfig:
-  lo        no wireless extensions.
-  
-  enp1s0    no wireless extensions.
+  lo        no wireless extensions.
+ 
+  enp1s0    no wireless extensions.
  Lsusb:
-  Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
-  Bus 001 Device 002: ID 08e6:4433 Gemalto (was Gemplus) GemPC433-Swap
-  Bus 001 Device 003: ID 0627:0001 Adomax Technology Co., Ltd QEMU Tablet
-  Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
+  Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
+  Bus 001 Device 002: ID 08e6:4433 Gemalto (was Gemplus) GemPC433-Swap
+  Bus 001 Device 003: ID 0627:0001 Adomax Technology Co., Ltd QEMU Tablet
+  Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
  Lsusb-t:
-  /:  Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/15p, 480M
-      |__ Port 001: Dev 002, If 0, Class=Chip/SmartCard, Driver=[none], 12M
-      |__ Port 002: Dev 003, If 0, Class=Human Interface Device, 
Driver=usbhid, 480M
-  /:  Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/15p, 5000M
+  /:  Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/15p, 480M
+      |__ Port 001: Dev 002, If 0, Class=Chip/SmartCard, Driver=[none], 12M
+      |__ Port 002: Dev 003, If 0, Class=Human Interface Device, 
Driver=usbhid, 480M
+  /:  Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/15p, 5000M
  MachineType: QEMU Standard PC (Q35 + ICH9, 2009)
  ProcEnviron:
-  LANG=en_US.UTF-8
-  PATH=(custom, no user)
-  SHELL=/bin/bash
-  TERM=xterm-256color
+  LANG=en_US.UTF-8
+  PATH=(custom, no user)
+  SHELL=/bin/bash
+  TERM=xterm-256color
  ProcFB: 0 virtio_gpudrmfb
  ProcKernelCmdLine: BOOT_IMAGE=/vmlinuz-6.8.0-87-generic 
root=/dev/mapper/ubuntu--vg-ubuntu--lv ro
  RelatedPackageVersions:
-  linux-restricted-modules-6.8.0-87-generic N/A
-  linux-backports-modules-6.8.0-87-generic  N/A
-  linux-firmware                            20240318.git3b128b60-0ubuntu2.19
+  linux-restricted-modules-6.8.0-87-generic N/A
+  linux-backports-modules-6.8.0-87-generic  N/A
+  linux-firmware                            20240318.git3b128b60-0ubuntu2.19
  RfKill:
-  
+ 
  SourcePackage: linux
  UpgradeStatus: No upgrade log present (probably fresh install)
  dmi.bios.date: 10/08/2025
  dmi.bios.release: 0.0
  dmi.bios.vendor: Ubuntu distribution of EDK II
  dmi.bios.version: 2025.02-8ubuntu3
  dmi.chassis.type: 1
  dmi.chassis.vendor: QEMU
  dmi.chassis.version: pc-q35-10.1
  dmi.modalias: 
dmi:bvnUbuntudistributionofEDKII:bvr2025.02-8ubuntu3:bd10/08/2025:br0.0:svnQEMU:pnStandardPC(Q35+ICH9,2009):pvrpc-q35-10.1:cvnQEMU:ct1:cvrpc-q35-10.1:sku:
  dmi.product.name: Standard PC (Q35 + ICH9, 2009)
  dmi.product.version: pc-q35-10.1
  dmi.sys.vendor: QEMU

** Also affects: linux (Ubuntu Resolute)
   Importance: Undecided
     Assignee: Massimiliano Pellizzer (mpellizzer)
       Status: Confirmed

** Also affects: linux (Ubuntu Noble)
   Importance: Undecided
       Status: New

** Also affects: linux (Ubuntu Jammy)
   Importance: Undecided
       Status: New

** Also affects: linux (Ubuntu Plucky)
   Importance: Undecided
       Status: New

** Also affects: linux (Ubuntu Questing)
   Importance: Undecided
       Status: New

** Changed in: linux (Ubuntu Resolute)
       Status: Confirmed => In Progress

** Changed in: linux (Ubuntu Questing)
       Status: New => In Progress

** Changed in: linux (Ubuntu Plucky)
       Status: New => In Progress

** Changed in: linux (Ubuntu Noble)
       Status: New => In Progress

** Changed in: linux (Ubuntu Jammy)
       Status: New => In Progress

** Changed in: linux (Ubuntu Jammy)
     Assignee: (unassigned) => Massimiliano Pellizzer (mpellizzer)

** Changed in: linux (Ubuntu Noble)
     Assignee: (unassigned) => Massimiliano Pellizzer (mpellizzer)

** Changed in: linux (Ubuntu Plucky)
     Assignee: (unassigned) => Massimiliano Pellizzer (mpellizzer)

** Changed in: linux (Ubuntu Questing)
     Assignee: (unassigned) => Massimiliano Pellizzer (mpellizzer)

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2131046

Title:
  CAP_PERFMON insufficient to get perf data

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2131046/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to