用CAP从bash脚本启动perf

我想利用功能来运行一些带有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

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

这种方法有什么问题?


脚本文件通常禁用它们的suid位(无论是在内核还是在某些shell解释器中),似乎对功能有类似的影响(并且脚本文件实际上是使用诸如bash ./scriptfile类的解释器启动的,所以脚本文件中的功能可能不是由进程继承):

  • https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts
  • https://unix.stackexchange.com/questions/87348/capabilities-for-a-script-on-linux
  • 使用小型简单编译程序来调用exec / execve执行并设置二进制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;
    ...
    

    我想出了利用@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 %sn",MY_CAPABILIY);
        execve("/usr/bin/perf",argv,envp);     //exec perf 
        //restore capabilties
        if(cap_set_proc(old_cap)<0) fprintf(stderr, "Error on capability restoren" );
        return 0;
    }
    

    我们称之为perf_wrapper上面的代码生成的可执行文件。 它用libcap编译(将选项-lcap添加到gcc )。 可执行文件简单地将MY_CAPABILITY设置为进程的功能,然后运行perf(或其他命令)。 但是,这不足以使用CAP_SYS_ADMIN运行perf,因为perf的可执行文件没有任何功能。 为了让事情顺利进行,还需要在perf可执行文件中添加一些功能。

    步骤如下:

  • sudo setcap cap_sys_admin+ei /usr/bin/perf ,这将CAP_SYS_ADMIN的perf功能设置为有效且可继承(同样允许设置将不允许普通用户在没有功能的情况下运行perf)。
  • sudo setcap cap_sys_admin+eip perf_wrapper设置的能力perf_wrapper
  • 现在可以通过执行perf_wrapper来使用CAP_SYS_ADMIN中的perf并像通常的bash脚本中的perf一样传递params。

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

    链接地址: http://www.djcxy.com/p/80043.html

    上一篇: Launching perf from bash script with CAP

    下一篇: Building Perf with Babeltrace (for Perf to CTF Conversion)