使用dlopen,我如何处理我加载的库文件的更改?

时间:2009-10-15 17:02:23

标签: c++ linux dlopen

我有一个用C ++编写的程序,它使用dlopen加载动态库(Linux,i386,.so)。随后修改库文件时,我的程序会崩溃。这是可以理解的,因为可能是文件只是映射到内存中。

我的问题是:除了简单地创建自己的文件副本和倾斜之外,我是否有办法加载一个可以安全地防止后续修改的共享对象,或者从修改共享对象的任何方式恢复我装了?

澄清:问题不是“如何在不崩溃程序的情况下安装新库”,“如果我不控制的人正在复制库,是否可能我要反抗那个?“

5 个答案:

答案 0 :(得分:18)

如果您在安装新库之前rm,我认为您的系统将保持分配的inode,文件打开以及您的程序正在运行。 (当你的程序最终退出时,大多数隐藏但仍然存在的文件资源被释放。)

更新:好的,后澄清。动态链接器实际上通过将MAP_COPY标志(如果可用)传递给mmap(2)来完全“解决”此问题。但是,MAP_COPY在Linux上不存在,并且不是计划的未来功能。第二个最好的是MAP_DENYWRITE,我相信加载器确实使用了,它在Linux API中,以及Linux曾经使用过的。在映射区域时,它会出错。它应该仍然允许rm并替换。这里的问题是任何对文件具有读访问权限的人都可以映射它并阻止写入,这会打开一个本地DoS漏洞。 (考虑/etc/utmp。有人建议使用执行权限位来修复此问题。)

你不会喜欢这个,但是有一个简单的内核补丁可以恢复MAP_DENYWRITE功能。 Linux仍具有该功能,它只是在mmap(2)的情况下清除该位。您必须在每个架构重复的代码中修补它,对于ia32,我认为该文件是arch/x86/ia32/sys_ia32.c

asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len,
                            unsigned long prot, unsigned long flags,
                            unsigned long fd, unsigned long pgoff)
{
        struct mm_struct *mm = current->mm;
        unsigned long error;
        struct file *file = NULL;

        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); // fix this line to not clear MAP_DENYWRITE

只要您没有任何具有凭据的恶意本地用户,这应该没问题。它不是一个远程的DoS,只是一个本地的。

答案 1 :(得分:6)

如果安装了新版本的库,则正确的步骤是在同一目录中创建一个新文件,然后将其重命名为旧文件。旧文件在打开时将保留,并继续使用。

像RPM这样的软件包管理器会自动执行此操作 - 因此您可以在共享库和可执行文件运行时更新它们,但旧版本会继续运行。

如果您需要使用新版本,请重新启动该过程或重新加载库 - 重新启动过程听起来更好 - 您的程序可以自行执行。即使是init也可以做到这一点。

答案 2 :(得分:4)

如果他们具有文件写入权限,则无法防止覆盖您图书馆的人。

因为dlopen内存映射了库文件,所以对文件的所有更改都会在打开它的每个进程中都可见。

dlopen函数使用内存映射,因为它是使用共享库的最有效内存的方法。私人拷贝会浪费内存。

正如其他人所说,在Unix中替换共享库的正确方法是使用unlink或rename, not 来用新副本覆盖库。 install命令可以正确执行此操作。

答案 3 :(得分:1)

如果你可以找出你的库被映射到内存的位置,那么你可以mprotect它可写,并对每个页面进行简单的写操作(例如,读取和写回每页的第一个字节) 。这应该会为您提供每个页面的私人副本。

如果'mprotect'不起作用(可能没有,原始文件可能是只读的),那么您可以将该区域复制到另一个位置,重新映射该区域(使用mmap)一个私人的可写区域,然后复制该区域。

我希望操作系统“将此只读区域转换为写入时复制区域”。不过,我不认为这样的事情存在。

在任何这些情况下,仍然存在漏洞窗口 - 有人可以在dlopen调用初始化程序时或在重新映射调用发生之前修改库。除非你能像@DigitalRoss描述的那样修复动态链接器,否则你并不安全。

无论如何,谁正在编辑您的图书馆?找到那个人并用煎锅打他的头。

答案 4 :(得分:0)

这是一个有趣的问题。我讨厌在Linux中找到这样的漏洞,并且喜欢找到修复它们的方法。

我的建议受到@Paul Tomblin对this question about temporary files on Linux的回答的启发。这里的一些其他答案表明存在这种机制,但没有描述从客户端应用程序中获取它的方法。

我没有测试过这个,所以我不知道它会有多好用。此外,与创建临时文件和解除链接之间的短暂时间段相关的竞争条件可能存在较小的安全问题。此外,您已经注意到可以创建库的副本,这正是我提出的。我对此的看法是,您的临时副本仅作为文件系统中的条目存在,而不管您实际打开库的时间长短。

如果要加载库,请按以下步骤操作:

  1. 将文件复制到临时位置,可能以mkstemp()
  2. 开头
  3. 使用dlopen()
  4. 加载库的临时副本
  5. 取消链接()临时文件
  6. 正常进行,dlclose()
  7. 时会自动删除文件的资源

    如果有一种非常简单的方法来实现“复制文件”步骤而不需要您实际复制文件,那将是很好的。我想到了硬连接,但我不认为它可以用于这些目的。如果Linux有一个像link()一样容易使用的写时复制机制,那将是理想的,但我不知道这样的设施。

    编辑:@Zan Lynx的回答指出,如果将动态库复制到多个进程中,那么创建动态库的自定义副本可能会非常浪费。所以我的建议可能只有在明智地应用时才有意义 - 只有那些有被踩踏风险的库(可能是所有库中不包含/ lib或/usr/lib中文件的小子集)。 p>