You need to enable JavaScript to run this app. 覆盖libc open()函数导致Python os.popen().read()调用挂死问题问询
优惠活动
大模型
产品
解决方案
定价
更多

覆盖libc open()函数导致Python os.popen().read()调用挂死问题问询

问题根因
  • 初始化循环依赖:全局对象g_sys_funcs的构造函数在共享库加载阶段执行,你在构造函数中调用dlopen加载libc.so.6时,dlopen内部需要打开动态库文件,会触发你已经预加载的自定义open函数。但此时g_sys_funcs还没完成构造,sys_open函数指针是空值,自定义open的逻辑执行异常,会返回错误的文件描述符。
  • 标准IO锁继承问题:你在自定义open中调用了printf,标准IO函数内部持有全局互斥锁保证线程安全。当Python的os.popen调用fork创建子进程时,如果父进程刚好持有printf的锁,子进程会继承这个锁的持有状态,子进程后续调用任何标准IO函数都会陷入无限等锁的状态。
    你碰到的按回车恢复的现象,是因为异常逻辑返回的文件描述符刚好指向了标准输入,popen的读操作直接从标准输入读取数据,输入回车后read拿到数据就会继续执行。
解决方案

可以通过以下几个修改彻底解决问题:

  1. 移除全局对象初始化逻辑,改为第一次调用open时的延迟初始化,完全避开共享库加载阶段的循环依赖。
  2. 不要用dlsym获取libc的open函数指针,直接调用系统调用SYS_open/SYS_openat来执行实际的打开操作,从根源上避免循环调用。
  3. 自定义open中不要使用任何标准IO函数(printf/puts等),如果需要打印日志直接调用write系统调用输出,避免标准IO锁的继承问题。
  4. 同时hookopen64函数,64位系统下绝大多数程序(包括你用的Python 3.6)会调用open64来支持大文件,仅hook open会漏掉大量调用。

修改后的代码示例如下:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>

// 延迟初始化标记
static volatile int g_init_done = 0;

static void init_sys_funcs() {
    if (__sync_bool_compare_and_swap(&g_init_done, 0, 1)) {
        // 直接使用系统调用不需要额外初始化libc函数指针
    }
}

static int realopen(const char *path, int flags, va_list args) {
    if (flags & (O_CREAT | O_TMPFILE)) {
        mode_t mode = va_arg(args, mode_t);
        return syscall(SYS_open, path, flags, mode);
    } else {
        return syscall(SYS_open, path, flags);
    }
}

extern "C" __attribute__((visibility ("default")))
int open(const char *pathname, int flags, ...) {
    if (!g_init_done) init_sys_funcs();
    // 用write系统调用打日志,规避标准IO锁问题
    const char log_prefix[] = "---- custom open ";
    write(STDOUT_FILENO, log_prefix, sizeof(log_prefix)-1);
    write(STDOUT_FILENO, pathname, strlen(pathname));
    write(STDOUT_FILENO, "\n", 1);

    va_list args;
    va_start(args, flags);
    int fd = realopen(pathname, flags, args);
    va_end(args);
    return fd;
}

// 同时hook open64兼容大文件调用
extern "C" __attribute__((visibility ("default")))
int open64(const char *pathname, int flags, ...) {
    if (!g_init_done) init_sys_funcs();
    const char log_prefix[] = "---- custom open64 ";
    write(STDOUT_FILENO, log_prefix, sizeof(log_prefix)-1);
    write(STDOUT_FILENO, pathname, strlen(pathname));
    write(STDOUT_FILENO, "\n", 1);

    va_list args;
    va_start(args, flags);
    int fd;
    if (flags & (O_CREAT | O_TMPFILE)) {
        mode_t mode = va_arg(args, mode_t);
        fd = syscall(SYS_open, pathname, flags | O_LARGEFILE, mode);
    } else {
        fd = syscall(SYS_open, pathname, flags | O_LARGEFILE);
    }
    va_end(args);
    return fd;
}

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 03:27:03