如何使用 setuid 或 capability CAP_SYS_RESOURCE 以编程方式提高实时优先级的 ulimit 硬限制?

How to raise ulimit hard limit for real time priority programmatically with setuid or capability CAP_SYS_RESOURCE?

我想运行下一个程序linuxSCHED_FIFO实时class。我宁愿将 RTPRIO 的用户硬限制设置为 0,并以编程方式提高单个进程的硬限制。人们普遍声称,如果我授予进程 CAP_SYS_RESOURCE 以允许它提高硬限制,例如, man setrlimit 2:

The soft limit is the value that the kernel enforces for the corresponding resource. The hard limit acts as a ceiling for the soft limit: an unprivileged process may only set its soft limit to a value in the range from 0 up to the hard limit, and (irreversibly) lower its hard limit. A privileged process (under Linux: one with the CAP_SYS_RESOURCE capability) may make arbitrary changes to either limit value.

但是,我似乎无法让它为我工作。这是测试代码:

#include <stdio.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <sys/resource.h>

#define PRIORITY (50)

int main(int argc, char **argv) {
  struct sched_param param;
  struct rlimit rl;
  int e, min_fifo, max_fifo;

  min_fifo = sched_get_priority_min(SCHED_FIFO);
  max_fifo = sched_get_priority_max(SCHED_FIFO);
  printf("For policy SCHED_FIFO min priority is %d, max is %d.\n",
         min_fifo, max_fifo);
  if ((min_fifo>PRIORITY)||(max_fifo<PRIORITY)) {
    printf("Desired priority of %d is out of range.\n", PRIORITY);
    return 1;
  }

  if (getrlimit(RLIMIT_RTPRIO, &rl) != 0) {
    e = errno;
    printf("Failed to getrlimit(): %s.\n", strerror(e));
    return 1;
  }
  printf("RTPRIO soft limit is %d, hard is %d.\n", 
         (int) rl.rlim_cur, (int) rl.rlim_max);

  // Adjust hard limit if necessary

  if (rl.rlim_max < PRIORITY) {
    rl.rlim_max = PRIORITY;
    if (setrlimit(RLIMIT_RTPRIO, &rl) != 0) {
      e = errno;
      printf("Failed to raise hard limit for RTPRIO to %d: %s.\n", 
             (int) rl.rlim_max, strerror(e));
      return 1;
    }
    printf("Raised hard limit for RTPRIO to %d.\n", (int) rl.rlim_max);
  }

  // Adjust soft limit if necessary

  if (rl.rlim_cur < PRIORITY) {
    rl.rlim_cur = PRIORITY;
    if (setrlimit(RLIMIT_RTPRIO, &rl) != 0) {
      e = errno;
      printf("Failed to raise soft limit for RTPRIO to %d: %s.\n", 
             (int) rl.rlim_cur, strerror(e));
      return 1;
    }
    printf("Raised soft limit for RTPRIO to %d.\n", (int) rl.rlim_cur);
  }

  // Set desired priority with class SCHED_FIFO

  param.sched_priority = PRIORITY;
  if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
    e = errno;
    printf("Setting policy failed: %s.\n", strerror(e));
    return 1;
  } else {
    printf("Set policy SCHED_FIFO, priority %d.\n", param.sched_priority);
  }

  return 0;
}

这在没有特殊权限的情况下按预期工作,硬限制为 99:

$ ./rtprio
For policy SCHED_FIFO min priority is 1, max is 99.
RTPRIO soft limit is 0, hard is 99.
Raised soft limit for RTPRIO to 50.
Set policy SCHED_FIFO, priority 50.
$

它按预期工作,硬限制为 0,使用 sudo:

$ sudo ./rtprio
For policy SCHED_FIFO min priority is 1, max is 99.
RTPRIO soft limit is 0, hard is 0.
Raised hard limit for RTPRIO to 50.
Raised soft limit for RTPRIO to 50.
Set policy SCHED_FIFO, priority 50.
$

但是当 setuid root:

时它并没有像预期的那样工作
$ sudo chown root ./rtprio
$ sudo chgrp root ./rtprio
$ sudo chmod ug+s ./rtprio
$ ls -l ./rtprio
-rwsrwsr-x 1 root root 8948 11月 28 12:04 ./rtprio
$ ./rtprio
For policy SCHED_FIFO min priority is 1, max is 99.
RTPRIO soft limit is 0, hard is 0.
Failed to raise hard limit for RTPRIO to 50: Operation not permitted.

它也意外地失败了 CAP_SYS_RESOURCE 以及 all 能力:

$ sudo setcap cap_sys_resource=eip ./rtprio
$ getcap ./rtprio
./rtprio = cap_sys_resource+eip
$ ./rtprio
For policy SCHED_FIFO min priority is 1, max is 99.
RTPRIO soft limit is 0, hard is 0.
Failed to raise hard limit for RTPRIO to 50: Operation not permitted.

$ sudo setcap all=eip ./rtprio
$ getcap ./rtprio
./rtprio =eip
$ ./rtprio
For policy SCHED_FIFO min priority is 1, max is 99.
RTPRIO soft limit is 0, hard is 0.
Failed to raise hard limit for RTPRIO to 50: Operation not permitted.

我在这里错过了什么?

$ uname -srv
Linux 3.13.0-100-generic #147-Ubuntu SMP Tue Oct 18 16:48:51 UTC 2016
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.5 LTS
Release:    14.04
Codename:   trusty
$ bash --version | head -1
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)

setuid root 无效的事实就是线索。

原来上面的测试程序是在挂载了nosuid的分区,所以setuid位没有作用。如果您不信任分区中的 setuid 位,那么您可能也不应该信任文件功能。而且,事实上,当使用 nosuid 挂载时,文件功能也会被忽略。

似乎 luks 加密的主目录倾向于挂载 nosuid

我把这个问题留了下来,因为 "linux capabilities nosuid" 的搜索引擎点击率很高,表明在这个问题上浪费了很多时间(当然你不知道去搜索直到你弄明白为止)。