【问题标题】:Avoiding internal threads in a shared object避免共享对象中的内部线程
【发布时间】:2018-03-01 11:18:11
【问题描述】:

我们有一个产品可以加载一个共享对象,该对象使用“dll 注入”和“检测”技术监控随机进程(我们称之为 myDb)中的内部函数调用。

目前,我们的共享对象有一个内部线程,用于接收来自我们主进程的消息,该主进程是一个外部实体。

在 myDb fork() 之前一切正常,并创建了一个不以 execve() 结尾的相同子进程。这个相同的子进程主线程在调用 fork() 的父线程的上下文中启动,并且所有其他父线程都不再存在于子线程中。这可能会破坏我们的共享对象内部状态,因为我们的内部线程可能会在任何执行点消失(请参阅http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them 中有关混合线程与分叉的更多信息)。

MyDb 可以在不询问我们的情况下使用 fork,因此,如果我错了,请纠正我,看来我们别无选择,只能让我们的代码在没有内部线程的情况下工作。

我能想到的唯一选择是异步 i/o。 根据http://man7.org/linux/man-pages/man7/aio.7.html,来自异步例程的通知是使用 sigevent 完成的。 sigevent 可以使用 SIGEV_SIGNAL 来使用信号或使用 SIGEV_THREAD 接收通知,根据手册页,它使用下面的实际线程。 因此,在我看来,我们唯一的选择是使用带有 SIGEV_SIGNAL 选项的异步 i/o。但这也有很多限制,因为在信号处理程序中处理消息是危险的,因为只能调用异步信号安全函数。

我很乐意在这里对我的问题提出任何建议。

谢谢。

【问题讨论】:

  • MyDb 可以在不询问我们的情况下使用 fork,因此,如果我错了,请纠正我,看来我们别无选择,只能让我们的代码在没有内部线程的情况下工作。 但是如果你这样做,你的代码如何知道它是原始父进程还是分叉子进程?无论您处于何种状态,都将在子节点中进行快照和复制,如果您处于事务中间,则可能处于不一致的状态。异步 IO 不会解决这个问题,甚至可能让事情变得更糟 - 子进程将如何处理看似已调度但真正属于父进程的 IO 操作?
  • 你是对的。这就是为什么我们还要拦截 fork() 调用。我还遇到了 pthread_atfork(),理论上它可以帮助我同步 fork() 并避免损坏,但如果可能的话,我更喜欢另一种方法。
  • 我可以提供替代解决方案吗?不要在随机的其他进程中注入代码。当另一个“有用的”进程将自己注入到包括我们在内的随机其他进程中时,我不得不花费相当多的时间来调试“我们的”软件。所以不是我们的代码崩溃了,尽管我们认为它是“我们的”进程。只有当注入器和受害者是由同一个组织编写时,注入才是可行的,最好是由同一个团队编写,这样它们就可以一起进行测试。
  • 我们这样做显然不是为了好玩——它是一种安全产品。别担心,当供应商看到我们的代码映射到内存空间时,他们做的第一件事就是禁用我们的产品。
  • 您可以使用执行ptrace(PTRACE_O_TRACEFORK, ...) 的ptracer 进程(在您自己注入的线程允许跟踪该进程之后)捕获所有fork() 调用,以便您可以让新的孩子“执行" 一个同样注入的函数来重新创建观察者线程(通过寄存器操作;需要一个预先准备好的一次性“堆栈”)。低开销,高复杂度;可以确保安全(尽管大多数开发人员,包括对安全敏感的软件开发人员,都不会打扰)。

标签: c linux unix asynchronous shared-libraries


【解决方案1】:

我发现的最佳解决方案是使用pthread_atfork() 注册处理程序。 此 API 可以帮助将fork() 与驻留在进程中的其他线程同步,以避免内存不一致。

更多内容在https://linux.die.net/man/3/pthread_atfork

不幸的是,使用pthread_atfork() 解决这个问题并不总是那么容易,因为每个进程的线程都必须合作,这是一个严格的假设。除此之外,它还破坏了我们的设计。

我们决定改变态度,使用jobs而不是threads。我来解释一下:

通常,在创建线程时,它们大部分时间都在某些调用(read()sleep() 等...)上阻塞,在这些情况下,线程并不是真正需要的,它可能更有意义改用作业。

我们开发了一个 JobScheduler() 类,该类将作业分派给已注册的作业(每次超时、fd 就绪读取等)。 现在,在这个类中,我们定义了一个分叉安全区,这是我们唯一允许分叉发生的区域,如果在正在进行的作业期间发生分叉,分叉将被延迟,直到作业结束。 这导致了一个限制,即作业执行必须相对较快,并且绝不会阻塞线程。

这个fork-safe-zone使我们能够在子进程中强制执行内存空间的一致性(使用pthread_atfork()),此外,我们还可以在child-post-fork回调中重新创建这个工作线程,这将使分叉限制不可见。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多