当我有正确的能力时,无法打开/proc/self/oom\u score\u adj
我正试图设置一个过程的OOM killer分数调整,灵感来自。为此,我打开当我有正确的能力时,无法打开/proc/self/oom\u score\u adj,c,linux,C,Linux,我正试图设置一个过程的OOM killer分数调整,灵感来自。为此,我打开/proc/self/oom\u score\u adj,读取旧值,然后写入新值。显然,我的进程需要是根进程,或者具有CAP\u SYS\u RESOURCE这样做的能力 我得到了一个无法解释的结果。当我的进程不具备该功能时,我可以打开该文件并读取和写入值,尽管我写入的值没有生效(足够公平): 但当我的进程确实具备此功能时,我甚至无法打开该文件:它在EACCES中失败: $ sudo setcap CAP_SYS_RESO
/proc/self/oom\u score\u adj
,读取旧值,然后写入新值。显然,我的进程需要是根进程,或者具有CAP\u SYS\u RESOURCE
这样做的能力
我得到了一个无法解释的结果。当我的进程不具备该功能时,我可以打开该文件并读取和写入值,尽管我写入的值没有生效(足够公平):
但当我的进程确实具备此功能时,我甚至无法打开该文件:它在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
为什么会这样?我错过了什么
进一步的谷歌搜索让我找到了。显然,
CAP\u SYS\u RESOURCE
允许您更改除您自己之外的任何流程的oom\u score\u adj
。要更改自己的分数调整,您需要将其与CAP\u DAC\u OVERRIDE
相结合,即禁用所有文件的访问控制。(如果我想要的话,我会让这个程序成为setuid root。)
所以我的问题是,如果没有CAP\u DAC\u OVERRIDE
,我如何实现这一点
我正在运行UbuntuXenial 16.04.4,内核版本4.13.0-45-generic。我的问题类似于但不同于:这是关于
写入时发生错误,当时没有该功能
我的示例程序:
#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;
}
#包括
#包括
#包括
#包括
无效读取值(文件*fp)
{
int值;
倒带(fp);
如果(fscanf(fp、%d、&value)!=1){
fprintf(stderr,“读取失败:%s\n”,ferror(fp)?strerror(errno):“无法解析”);
}
否则{
fprintf(标准,“oom\u得分调整值:%d\n”,值);
}
}
无效写入值(文件*fp)
{
int结果;
倒带(fp);
结果=fprintf(fp,“-1000”);
如果(结果<0){
fprintf(stderr,“写入失败:%s\n”,strerror(errno));
}
否则{
fprintf(stderr,“写入%d字节\n”,结果);
}
}
int main()
{
文件*fp;
结构uu用户u封顶u标题u结构h;
结构uu用户u cap u数据u结构d;
h、 版本=\u LINUX\u功能\u版本\u 3;
h、 pid=0;
如果(0!=capget(&h,&d)){
fprintf(stderr,“capget失败:%s\n”,strerror(errno));
}
否则{
fprintf(标准,“CAP系统资源:%s,%s,%s\n”,
d、 有效&(1这一个非常有趣,我花了一段时间才破解
第一个真正的暗示是对另一个问题的回答:-只是想给予信任
它不能正常工作的原因是
你被“拒绝许可”的真正原因如果进程具有任何功能,则/proc/self/
下的文件归root所有-这不是关于CAP\u SYS\u RESOURCE
或关于oom*
文件。您可以通过调用stat
并使用不同的功能来验证这一点。引用man 5 proc
:
/过程/[pid]
每个正在运行的进程都有一个数字子目录;子目录由进程ID命名
每个/proc/[pid]子目录都包含下面描述的伪文件和目录。这些文件通常由进程的有效用户和有效组ID拥有。但是,作为安全措施,如果进程的“可转储”,则将所有权设为root:root属性设置为1以外的值。此属性可能会因以下原因而更改:
- 该属性是通过prctl(2)PR_set_DUMPABLE操作显式设置的
- 由于prctl(2)中描述的原因,该属性被重置为文件/proc/sys/fs/suid_dumpable(如下所述)中的值
将“dumpable”属性重置为1会将/proc/[pid]/*文件的所有权恢复为进程的真实UID和真实GID
这已经暗示了解决方案,但首先让我们深入一点,看看manprctl
:
PR_SET_DUMPABLE(从Linux 2.3.20开始)
设置“dumpable”标志的状态,该标志确定在传递默认行为为生成核心转储的信号时,是否为调用进程生成核心转储
在2.6.12之前(含2.6.12)的内核中,arg2必须是0(SUID_DUMP_DISABLE,进程不可转储)或1(SUID_DUMP_USER,进程可转储)。在内核2.6.13和2.6.17之间,还允许使用值2,这导致任何通常不会转储的二进制文件只能由root读取转储;出于安全原因,此功能已被删除。(另请参见proc(5)中的/proc/sys/fs/suid_dumpable说明。)
通常,此标志设置为1。但是,在以下情况下,它将重置为文件/proc/sys/fs/suid_dumpable中包含的当前值(默认值为0):
- 进程的有效用户或组ID已更改
- 进程的文件系统用户或组ID已更改(请参阅凭据(7))
- 该进程执行(execve(2))设置用户ID或设置组ID程序,导致有效用户ID或有效组ID的更改
- 进程执行(execve(2))一个具有文件功能的程序(请参阅功能(7)),但前提是获得的允许功能超过了进程已经允许的功能。
无法通过ptrace(2)ptrace_ATTACH连接不可转储的进程;有关更多详细信息,请参阅ptrace(2)
如果进程不可转储,则进程的/proc/[pid]目录中文件的所有权将受到影响,如proc(5)所述
现在很清楚了:我们的进程具有用于启动它的shell所没有的功能,因此dumpable属性被设置为false,因此/proc/self/
下的文件由root用户而不是当前用户拥有
如何让它工作
修复方法非常简单,只需在尝试恢复之前重新设置可转储属性
#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;
}
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);