понедельник

[Bug 2131046] Re: CAP_PERFMON insufficient to get perf data

This bug is awaiting verification that the linux/6.8.0-91.92 kernel in
-proposed solves the problem. Please test the kernel and update this bug
with the results. If the problem is solved, change the tag
'verification-needed-noble-linux' to 'verification-done-noble-linux'. If
the problem still exists, change the tag 'verification-needed-noble-
linux' to 'verification-failed-noble-linux'.


If verification is not done by 5 working days from today, this fix will
be dropped from the source code, and this bug will be closed.


See https://wiki.ubuntu.com/Testing/EnableProposed for documentation how
to enable and use -proposed. Thank you!


** Tags added: kernel-spammed-noble-linux-v2 verification-needed-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", &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
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

Комментариев нет:

Отправить комментарий