【发布时间】:2018-06-21 21:00:19
【问题描述】:
我正在尝试为流程设置 OOM 杀手分数调整,灵感来自 oom_adjust_setup in OpenSSH's port_linux.c。为此,我打开/proc/self/oom_score_adj,读取旧值并写入新值。显然,我的进程需要是 root 或具有CAP_SYS_RESOURCE 的能力才能做到这一点。
我得到了一个我无法解释的结果。当我的进程没有能力时,我可以打开该文件并读取和写入值,尽管我写入的值没有生效(很公平):
$ ./a.out
CAP_SYS_RESOURCE: not effective, not permitted, not inheritable
oom_score_adj value: 0
wrote 5 bytes
oom_score_adj value: 0
但是当我的进程确实有能力时,我甚至无法打开文件:它因 EACCES 而失败:
$ sudo setcap CAP_SYS_RESOURCE+eip a.out
$ ./a.out
CAP_SYS_RESOURCE: effective, permitted, not inheritable
failed to open /proc/self/oom_score_adj: Permission denied
为什么要这样做?我错过了什么?
进一步的谷歌搜索让我找到了this lkml post by Azat Khuzhin on 20 Oct 2013。显然CAP_SYS_RESOURCE 可以让您更改oom_score_adj 以用于除您自己以外的任何进程。要更改您自己的分数调整,您需要将其与CAP_DAC_OVERRIDE 结合使用——即禁用所有文件的访问控制。 (如果我想要的话,我会把这个程序设置为 root。)
所以我的问题是,如果没有CAP_DAC_OVERRIDE,我如何实现这一目标?
我正在运行 Ubuntu xenial 16.04.4,内核版本 4.13.0-45-generic。我的问题与this question 相似但不同:这是关于write 的错误,当没有能力时。
我的示例程序:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/capability.h>
void read_value(FILE *fp)
{
int value;
rewind(fp);
if (fscanf(fp, "%d", &value) != 1) {
fprintf(stderr, "read failed: %s\n", ferror(fp) ? strerror(errno) : "cannot parse");
}
else {
fprintf(stderr, "oom_score_adj value: %d\n", value);
}
}
void write_value(FILE *fp)
{
int result;
rewind(fp);
result = fprintf(fp, "-1000");
if (result < 0) {
fprintf(stderr, "write failed: %s\n", strerror(errno));
}
else {
fprintf(stderr, "wrote %d bytes\n", result);
}
}
int main()
{
FILE *fp;
struct __user_cap_header_struct h;
struct __user_cap_data_struct d;
h.version = _LINUX_CAPABILITY_VERSION_3;
h.pid = 0;
if (0 != capget(&h, &d)) {
fprintf(stderr, "capget failed: %s\n", strerror(errno));
}
else {
fprintf(stderr, "CAP_SYS_RESOURCE: %s, %s, %s\n",
d.effective & (1 << CAP_SYS_RESOURCE) ? "effective" : "not effective",
d.permitted & (1 << CAP_SYS_RESOURCE) ? "permitted" : "not permitted",
d.inheritable & (1 << CAP_SYS_RESOURCE) ? "inheritable" : "not inheritable");
}
fp = fopen("/proc/self/oom_score_adj", "r+");
if (!fp) {
fprintf(stderr, "failed to open /proc/self/oom_score_adj: %s\n", strerror(errno));
return 1;
}
else {
read_value(fp);
write_value(fp);
read_value(fp);
fclose(fp);
}
return 0;
}
【问题讨论】:
-
对于初学者,在
write_value(fp);之后,您需要在read_value(fp);之前再次rewind(fp);。否则,文件位置指示器为EOF,用于第二次读取。 -
我很困惑。你引用了一个消息来源说你不能做你说你想做的事。你有特别的理由怀疑这个来源吗?
-
据我所知,该邮件列表帖子从未得到权威答案。我想我的问题与该帖子中的问题相同:“这是设计使然,和/或是否有另一种方法可以在没有 suid/root 的情况下做到这一点?”