← 返回首页目录
# Linux 内核 Perf Events 与工具安全性深度解析

**作者:吉祥法师**

## 一、核心概念:Perf Events 的数据泄露风险

Performance Counters for Linux(perf_events)是内核提供的性能监控子系统,广泛用于系统性能分析与可观测性操作。然而,其监控能力本身构成一项被严重低估的安全风险——被监控进程所访问的敏感数据可能通过 perf_events 发生泄露。

泄露路径有两条:一是直接使用 perf_events 系统调用 API;二是通过 Perf 用户态工具生成的数据文件。风险程度取决于 perf_events 性能监控单元(PMU)与 Perf 所采集和暴露的数据性质。

### 采集数据的四个安全层级

理解访问控制的必要性,首先需要理解 perf_events 所采集数据的分类。按照敏感程度由低到高,可划分为四类:

**第一类:系统硬件与软件配置数据。** 包括 CPU 型号及其缓存配置、可用内存量与拓扑结构、内核与 Perf 版本、性能监控实验的时间设置、事件配置、Perf 命令行参数等。此类数据描述系统环境,本身不涉及进程行为隐私。

**第二类:路径、地址与标识数据。** 包括用户态与内核模块路径及其加载地址和大小、进程与线程名称及其 PID/TID、硬件与软件事件的时间戳。此类数据可揭示系统运行结构与进程调度模式。

**第三类:计数器内容数据。** 包括内核软件计数器(上下文切换、缺页、CPU 迁移等)、架构硬件性能计数器(PMC)以及机器特定寄存器(MSR)所提供的执行度量。这些数据来自内存控制器(IMC)、互连(QPI/UPI)或外设(PCIe)等 uncore 计数器,但**不直接归属任何执行上下文状态**。正因如此,其敏感度相对可控。

**第四类:执行上下文寄存器与进程内存数据。** 包括架构执行上下文寄存器内容(如 x86_64 上的 RIP、RSP、RBP)、进程用户态与内核态内存地址及数据、以及捕获此类数据的各类架构 MSR。**这是唯一可能包含敏感进程数据的一类。**

由此得出一个关键结论:若 PMU 在特定监控模式下能够捕获执行上下文寄存器值或进程内存数据,则该监控模式的访问必须被严格排序与保护。perf_events 的性能监控与可观测性操作因此被纳入安全访问控制管理范畴。

## 二、逻辑结构:访问控制的三层架构

Linux 内核对 perf_events 的访问控制遵循清晰的层次逻辑,可概括为三个层面。

### 第一层:进程分类与能力模型

内核首先将进程分为两类:**特权进程**(有效用户 ID 为 0,即 root)与**非特权进程**(有效 UID 非零)。特权进程绕过所有内核安全检查,可无限制使用 perf_events。非特权进程则需通过基于进程凭证的完整安全检查,凭证通常包括有效 UID、有效 GID 与补充组列表。

Linux 将传统超级用户特权拆分为独立的能力单元(capabilities),可在每线程基础上独立启用与禁用。这一机制构成了访问控制的基础。

### 第二层:CAP_PERFMON 能力的引入

**CAP_PERFMON** 是专为性能监控与可观测性操作设计的能力。拥有该能力的非特权进程在 perf_events 操作上被视为特权进程,可绕过内核的范围权限检查。

CAP_PERFMON 实现了性能监控领域的最小权限原则(POSIX 1003.1e: 2.2.2.39),提供了系统性能监控的安全方法。出于向后兼容,CAP_SYS_ADMIN 特权进程同样可访问 perf_events 监控操作,但在安全监控场景中**不鼓励使用 CAP_SYS_ADMIN**,应优先使用 CAP_PERFMON。

对于审计场景:若进程使用 perf_events 系统调用 API 的审计记录中同时包含获取 CAP_PERFMON 与 CAP_SYS_ADMIN 的拒绝记录,推荐单独授予 CAP_PERFMON 能力,作为解决双重访问拒绝日志的首选安全方案。

**版本演进要点:** Linux v5.9 之前,使用 perf_events 系统调用的非特权进程还需通过 PTRACE_MODE_READ_REALCREDS ptrace 访问模式检查,拥有 CAP_SYS_PTRACE 能力的非特权进程可有效通过该检查。从 Linux v5.9 起,CAP_SYS_PTRACE 不再是必需条件,CAP_PERFMON 已足够。

此外,授予非特权进程其他能力可能间接启用额外数据捕获。例如,CAP_SYSLOG 能力允许从 `/proc/kallsyms` 文件读取内核空间内存地址。

### 第三层:非特权进程的范围参数

对于非特权进程,perf_events 的范围与访问控制由 `perf_event_paranoid` 设置决定:

- **-1**:不施加任何范围与访问限制。内存缓冲区分配时忽略每用户每 CPU 的 `perf_event_mlock_kb` 锁定限制。这是**最不安全的模式**,允许的监控范围最大化,且无 perf_events 特定资源限制。
- **>=0**:范围包括每进程与系统级性能监控,但**排除**原始 tracepoint 与 ftrace 函数 tracepoint 监控。用户态与内核态执行的 CPU 与系统事件均可监控。施加 `perf_event_mlock_kb` 锁定限制,但拥有 CAP_IPC_LOCK 能力的非特权进程可忽略此限制。
- **>=1**:范围仅包括每进程性能监控,**排除系统级监控**。其余同 >=0。
- **>=2**:范围仅包括每进程性能监控,且**仅用户态执行**的 CPU 与系统事件可被监控捕获。其余同 >=0。

## 三、主要论点与论据:特权 Perf 用户组的构建

文档的核心实践论点是:通过能力机制、特权 capability-dumb 文件、文件系统 ACL 与 sudo 工具,可创建专用的特权 Perf 用户组,使其在无限制条件下执行性能监控与可观测性操作。

### 方案一:为 Perf 可执行文件分配能力

**论据与操作步骤:**

创建 `perf_users` 特权用户组,将 Perf 工具可执行文件归属该组,并限制非成员用户的访问:

```
# groupadd perf_users
# chgrp perf_users perf
# chmod o-rwx perf
```

为 Perf 可执行文件分配所需能力,使 `perf_users` 组成员获得监控与可观测性特权:

```
# setcap "cap_perfmon,cap_sys_ptrace,cap_syslog=ep" perf
# setcap -v "cap_perfmon,cap_sys_ptrace,cap_syslog=ep" perf
perf: OK
# getcap perf
perf = cap_sys_ptrace,cap_syslog,cap_perfmon+ep
```

**兼容性注意事项:** 若已安装的 libcap 尚不支持 "cap_perfmon",需使用数字 "38" 替代:

```
# setcap "38,cap_ipc_lock,cap_sys_ptrace,cap_syslog=ep" perf
```

对于 `perf top` 等工具,可能需要加入 `cap_ipc_lock`,或使用 `perf top -m N` 减少 perf 环形缓冲区内存占用。

若 libcap 不支持 CAP_PERFMON,`cap_get_flag(caps, 38, CAP_EFFECTIVE, &val)` 将失败,导致默认事件变为 `cycles:u`。变通方法是显式请求 `cycles` 事件:

```
# perf top -e cycles
```

如此便可在仅具备 CAP_PERFMON 的 perf 二进制文件下获取内核与用户态采样。

### 方案二:构建能力特权 Shell 环境

当 Perf 可执行文件无法分配所需能力时(例如文件系统以 `nosuid` 选项挂载,或文件系统不支持扩展属性),可创建能力特权环境(即特权 Shell)。

**实现逻辑:** Shell 为其固有进程提供 CAP_PERFMON 及其他所需能力,使性能监控操作在环境中无限制可用。通过 sudo 工具仅向 `perf_users` 组成员开放该环境的访问。

创建使用 `capsh` 工具的 Shell 脚本:

```bash
# cat /usr/local/bin/perf.shell
exec /usr/sbin/capsh --iab=^cap_perfmon --secbits=239 --user=$SUDO_USER -- -l
```

该脚本将 CAP_PERFMON 分配至 Shell 进程的环境能力集,启用 SECBIT_NO_SETUID_FIXUP、SECBIT_NOROOT 与 SECBIT_NO_CAP_AMBIENT_RAISE 位后锁定进程安全位,然后将进程身份切换为脚本的 sudo 调用者(应为 `perf_users` 组成员)。

扩展 `/etc/sudoers` 策略:

```
# grep perf_users /etc/sudoers
%perf_users ALL=/usr/local/bin/perf.shell
```

**验证结果:** 组成员通过 sudo 进入特权 Shell 后,`/proc/self/status` 显示 CapInh、CapPrm、CapEff、CapAmb 均包含 `0000004000000000`,解码即为 `cap_perfmon`。

此特定访问控制管理仅对拥有 CAP_SETPCAP、CAP_SETFCAP 能力的超级用户或 root 运行进程可用。

## 四、资源控制:文件描述符与内存分配

### 打开文件描述符限制

perf_events 系统调用 API 为每个配置的 PMU 事件分配文件描述符。打开的文件描述符是受 `RLIMIT_NOFILE`(`ulimit -n`)限制的每进程可计资源,通常继承自登录 Shell 进程。

在大规模服务器系统上为大量事件配置 Perf 采集时,此限制极易被触及,阻碍所需的监控配置。`RLIMIT_NOFILE` 限制可通过修改 `limits.conf` 文件按用户增加。

**关键计算规则:** 一次 Perf 采样会话(`perf record`)所需的打开 perf_event 文件描述符数量,不少于被监控事件数乘以被监控 CPU 数。

### 内存分配限制

用户进程可用于捕获性能监控数据的内存量由 `perf_event_mlock_kb` 设置控制。该 perf_event 特定资源设置定义了用户进程为执行性能监控而映射的每 CPU 内存总限制,实质上扩展了 `RLIMIT_MEMLOCK` 限制,但仅针对专门为捕获被监控性能事件及相关数据而映射的内存区域。

**计算示例:** 若机器有 8 个核心,`perf_event_mlock_kb` 设为 516 KiB,则用户进程在 `RLIMIT_MEMLOCK` 之上获得 516 KiB × 8 = 4128 KiB 的 perf_event mmap 缓冲区内存。

**实践含义:** 若用户希望启动两个或更多性能监控进程,需手动在监控进程间分配这 4128 KiB,例如使用 Perf record 模式的 `--mmap-pages` 选项。否则,首个启动的监控进程将分配全部 4128 KiB,其他进程因内存不足而失败。

拥有 CAP_IPC_LOCK 能力的进程可忽略 `RLIMIT_MEMLOCK` 与 `perf_event_mlock_kb` 资源约束。因此,通过为 Perf 可执行文件提供 CAP_IPC_LOCK 能力,可为 perf_events/Perf 特权用户提供超出约束的内存用于性能监控。

## 五、总结

本文围绕 Linux 内核 perf_events 的安全访问控制展开,核心结论可归纳为四点:

第一,perf_events 存在真实的数据泄露风险,第四类数据(执行上下文寄存器与进程内存)是敏感数据泄露的关键来源,必须对相应监控模式实施访问控制。

第二,CAP_PERFMON 能力是实现 perf_events 最小权限安全访问的核心机制,应优先于 CAP_SYS_ADMIN 使用;Linux v5.9 后不再要求 CAP_SYS_PTRACE。

第三,通过能力分配、特权文件、ACL 与 sudo,可构建 `perf_users` 特权用户组或能力特权 Shell 环境,实现受控的无限制性能监控。

第四,`perf_event_paranoid` 参数决定非特权进程的监控范围,`RLIMIT_NOFILE` 与 `perf_event_mlock_kb` 构成资源约束,CAP_IPC_LOCK 可解除内存限制。三者共同构成 perf_events 资源与范围管理的完整框架。