3

我想利用性能来运行一些带有 perf 的测试,而无需运行命令,root也无需调整/proc/sys/kernel/perf_event_paranoid. perf 的一些错误消息说:

You may not have permission to collect stats.
Consider tweaking /proc/sys/kernel/perf_event_paranoid,
which controls use of the performance events system by
unprivileged users (without CAP_SYS_ADMIN).
The current value is 2:
-1: Allow use of (almost) all events by all users
>= 0: Disallow raw tracepoint access by users without CAP_IPC_LOCK
>= 1: Disallow CPU event access by users without CAP_SYS_ADMIN
>= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN

所以我尝试通过以下方式创建一些具有相同源但不同功能集的 bash 脚本:

wrapper_no_cap.sh -> no capabilities set
wrapper_cap_ipc_lock.sh -> setcap cap_ipc_lock+eip ./wrapper_cap_ipc_lock.sh
wrapper_cap_sys_admin.sh -> setcap cap_sys_admin+eip ./wrapper_cap_sys_admin.sh

每个脚本都有相同的来源,如下所示:

#!/bin/bash
perf stat -e L1-dcache-load-misses:k seq 1 10

但是我运行的每个脚本都会给我一个结果,就好像我是一个普通用户一样(这意味着我不能计算内核事件或其他特权的东西)。就像我调用脚本时丢弃了功能一样。性能版本是4.11.ga351e9.

这种方法有什么问题?

4

2 回答 2

1

脚本文件通常禁用其 suid 位(在内核和某些 shell 解释器中),似乎对功能有类似的影响(并且脚本文件实际上是使用类似的解释器启动的bash ./scriptfile,因此脚本文件中的功能可能不会被进程继承):

使用小型简单编译程序通过 exec/execve 调用 perf 并在二进制 ELF 上设置功能。

Linux 内核中用于脚本启动的实际代码 - http://elixir.free-electrons.com/linux/v4.10/source/fs/binfmt_script.c - 使用解释器二进制文件,而不是脚本文件(旧bprm->file)来获取suid 等权限

/*
 * OK, now restart the process with the interpreter's dentry.
 */
file = open_exec(interp);
if (IS_ERR(file))
    return PTR_ERR(file);

bprm->file = file;
retval = prepare_binprm(bprm);

http://elixir.free-electrons.com/linux/v4.10/source/fs/exec.c#L1512

/*
 * Fill the binprm structure from the inode.
 * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
 *
 * This may be called multiple times for binary chains (scripts for example).
 */
int prepare_binprm(struct linux_binprm *bprm)
{
    int retval;

    bprm_fill_uid(bprm);

    /* fill in binprm security blob */
    retval = security_bprm_set_creds(bprm);
    if (retval)
        return retval;
    bprm->cred_prepared = 1;
  ...

static void bprm_fill_uid(struct linux_binprm *bprm)
{
    /* Be careful if suid/sgid is set */
    inode_lock(inode);

    /* reload atomically mode/uid/gid now that lock held */
    mode = inode->i_mode;
    uid = inode->i_uid;
    gid = inode->i_gid;
...
于 2017-05-22T14:18:02.090 回答
0

我想出了一个利用@osgx 建议的转机。

我写了这个包装 perf 的小 C 程序。这里是:

#include <unistd.h>
#include <stdio.h>
#include <sys/capability.h>

#define MY_CAPABILIY "cap_sys_admin+eip"

int main(int argc, char * argv[], char * envp[])
{
    cap_t old_cap=cap_get_proc(); //save current capabilities
    //getting MY_CAPABILITY associated struct
    cap_t csa = cap_from_text(MY_CAPABILIY);  
    //set capabilities
    if(cap_set_proc(csa)<0) fprintf(stderr,"cannot set %s\n",MY_CAPABILIY);
    execve("/usr/bin/perf",argv,envp);     //exec perf 
    //restore capabilties
    if(cap_set_proc(old_cap)<0) fprintf(stderr, "Error on capability restore\n" );
    return 0;
}

让我们调用从上面的代码生成的可执行文件perf_wrapper。它是用 libcap 编译的(添加选项-lcapgcc)。可执行文件只需将 MY_CAPABILITY 设置为进程的能力,然后运行 ​​perf(或其他命令)。但这不足以使用 CAP_SYS_ADMIN 运行 perf,因为可执行文件perf没有任何功能。为了让事情正常工作,还需要向 perf 可执行文件添加一些功能。

步骤如下:

  1. sudo setcap cap_sys_admin+ei /usr/bin/perf, 这将 CAP_SYS_ADMIN 的 perf 功能设置为有效且可继承(也设置 allowed 不会允许普通用户在没有功能的情况下运行 perf)。
  2. sudo setcap cap_sys_admin+eip perf_wrapper设置能力perf_wrapper

现在可以通过执行perf_wrapper和传递参数来将 perf 与 CAP_SYS_ADMIN 一起使用,就像普通 bash 脚本中的 perf 一样。

注意:我不是能力专家。我希望我没有犯大的安全错误。

于 2017-05-22T14:24:26.717 回答