APPLIED[Q/N/J + P/Cmnt]: [SRU][J/N/P/Q/Unstable][PATCH 0/1] CAP_PERFMON insufficient to get perf data

Stefan Bader stefan.bader at canonical.com
Thu Nov 13 14:07:04 UTC 2025


On 11/11/2025 16:34, Massimiliano Pellizzer wrote:
> BugLink: https://bugs.launchpad.net/bugs/2131046
> 
> [ 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.
> 
> Massimiliano Pellizzer (1):
>    UBUNTU: SAUCE: perf/core: Allow CAP_PERFMON for paranoid level 4
> 
>   kernel/events/core.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 

Note that plucky might not release as there is no SRU cycle left before 
it has EOL.
Applied to questing,plucky,noble,jammy:linux/master-next. Thanks.

-Stefan

-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_0xE8675DEECBEECEA3.asc
Type: application/pgp-keys
Size: 48643 bytes
Desc: OpenPGP public key
URL: <https://lists.ubuntu.com/archives/kernel-team/attachments/20251113/30228e34/attachment-0001.key>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <https://lists.ubuntu.com/archives/kernel-team/attachments/20251113/30228e34/attachment-0001.sig>


More information about the kernel-team mailing list