After installing a fresh noble VM...
I ran the repro code on the current and proposed kernel, the patch worked as intended.
```
:: Linux cap-noble 6.8.0-88-generic #89-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 01:02:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
[*] 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
:: Linux cap-noble 6.8.0-91-generic #92-Ubuntu SMP PREEMPT_DYNAMIC Fri
Nov 28 16:26:35 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
[*] 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
```
I also verfied that node_exporter exports perf metrics with
cap_perfmon=ep under the new kernel but not on older kernels.
** Tags removed: verification-needed-noble-linux
** Tags added: verification-done-noble-linux
--
You received this bug notification because you are subscribed to linux
in Ubuntu.
Matching subscriptions: Bgg, Bmail, Nb
https://bugs.launchpad.net/bugs/2131046
Title:
CAP_PERFMON insufficient to get perf data
Status in linux package in Ubuntu:
Fix Committed
Status in linux source package in Jammy:
Fix Committed
Status in linux source package in Noble:
Fix Committed
Status in linux source package in Plucky:
Fix Committed
Status in linux source package in Questing:
Fix Committed
Status in linux source package in Resolute:
Fix Committed
Bug description:
[ 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", ¶noid_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
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.
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
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
MachineType: QEMU Standard PC (Q35 + ICH9, 2009)
ProcEnviron:
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
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
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2131046/+subscriptions
Комментариев нет:
Отправить комментарий