open中的 O_CLOEXEC 标志

本文探讨了O_CLOEXEC标志的作用及其如何避免父进程的文件描述符泄露给子进程,通过示例代码展示了如果没有正确设置O_CLOEXEC,可能导致普通用户访问到无权限的文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

含义:在进程执行exec系统调用时关闭此打开的文件描述符。在子进程没有相应权限的情况下,防止父进程将打开的文件描述符泄露给子进程,防止子进程间接获得权限。

fd泄露引起普通用户访问无权限的文件:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
    int count;
    pid_t pid;
    char buf[1024] = {0};
    int fd;

    //以root身份打开文件
    fd = open("/etc/shadow", O_RDONLY);
    if( fd < 0 )
    {
	    perror("open /etc/shadow");
	    return -1;
    }

	pid = fork();
	if( 0 == pid )
	{
        seteuid(500);
        setegid(500);
        //fd是从父进程泄露出来,不受权限制约,可以读取文件
        printf("EUID:%d, UID:%d\n", geteuid(), getuid());
        while( (count = read(fd, buf, sizeof(buf))) > 0 )
        {
	        write(STDOUT_FILENO, buf, count);
        }
        
        close(fd);

        //普通用户! 无法打开文件
        fd = open("/etc/shadow", O_RDONLY);
        printf("fd=%d\n", fd);

        if( fd<0 )
        {            
            perror("open /etc/shadow");
            return -1;
        }
        return 0;
    }

    close(fd);
    return 0;
}

设置O_CLOEXEC一般是在open时设置,这个是原子操作;也可以用fcntl()的F_SETFD命令来设置,但它有并发危险,如多线程中,一个线程将要设置O_CLOEXEC标志时,虽一个线程fork(),且先得到执行,导致打开的文件描述符泄露到子进程中。

### 关于 `TEMP_FAILURE_RETRY` 函数在打开 `/dev/device-mapper` 时的应用与故障排除 #### 使用场景分析 当调用 `open()` 打开设备文件(如 `/dev/device-mapper`),可能会因为临时错误而失败。这些错误通常由信号中断或其他瞬态条件引起。为了处理这种情况,可以使用 GNU 提供的 `TEMP_FAILURE_RETRY` 宏[^1]。 该宏的作用是对指定表达式进行重试操作,直到其返回值不再表示因信号中断而导致的错误为止。具体实现如下: ```c #define TEMP_FAILURE_RETRY(expression) \ ({ \ typeof(expression) _result; \ do { \ _result = (expression); \ } while (_result == -1 && errno == EINTR); \ _result; \ }) ``` 通过上述定义可以看出,如果 `expression` 的结果为 `-1` 并且 `errno` 值为 `EINTR`,则会重复执行此表达式直至成功或发生其他类型的错误[^4]。 #### 实际应用案例 假设我们需要以读写模式 (`O_RDWR`) 和关闭执行标志 (`O_CLOEXEC`) 来打开 `/dev/device-mapper` 设备,则可按照以下方式编写代码片段: ```c #include <fcntl.h> #include <unistd.h> int fd; fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)); if (fd == -1) { perror("Failed to open /dev/device-mapper"); } else { printf("Successfully opened /dev/device-mapper\n"); } close(fd); ``` 在此例子中,我们利用了 `TEMP_FAILURE_RETRY` 对可能出现的短暂性错误进行了自动恢复尝试。 #### 故障排查建议 尽管有了这样的机制保护我们的 I/O 调用免受某些特定异常的影响,但在实际开发过程中仍需注意以下几个方面来进行有效的调试和维护工作: - **确认权限设置**:确保当前进程有足够的权限访问目标设备节点。 - **检查硬件状态**:有时物理存储介质本身存在问题也会引发类似的软件层面上看似无解的情况。 - **日志记录增强**:增加详细的日志输出可以帮助定位问题所在位置以及根本原因。 对于 Vue 或 Node.js 开发者来说,在面对诸如 “JavaScript heap out of memory” 错误时,除了调整虚拟机参数外,还应该考虑优化应用程序本身的性能表现,比如减少不必要的全局变量声明、及时释放未使用的资源对象等措施[^5]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值