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

我有一个用C ++编写的程序,它使用dlopen来加载一个dynamic库(Linux,i386,.so)。 当库文件随后被修改时,我的程序往往会崩溃。 这是可以理解的,因为大概文件被简单地映射到内存中。

我的问题是:除了简单地创build自己的文件和dlopening的副本,有没有办法让我加载一个共享的对象是安全的,以防止后续的修改,或任何方式来恢复修改我已经加载的共享对象?

澄清:问题不在于“如何在不使程序崩溃的情况下安装新的库”,而是“如果我不控制的人正在复制图书馆,是否有可能为此辩护?

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

更新 :好的,后澄清。 动态链接器实际上通过将MAP_COPY标志(如果可用)传递给mmap(2)来完全“解决”此问题。 但是, MAP_COPY在Linux上不存在,并不是计划的未来功能。 第二好的是MAP_DENYWRITE ,我相信加载器使用的是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,只是一个本地的。

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

像RPM这样的软件包管理器会自动执行这个操作 – 这样您就可以在运行时更新共享库和可执行文件,但旧版本仍在运行。

在需要新版本的情况下,重新启动进程或重新加载库 – 重新启动进程听起来更好 – 程序可以自行执行。 即使init可以做到这一点。

如果他们有文件写入权限,就不可能防止某人覆盖你的图书馆。

由于dlopen内存映射库文件,文件的所有更改在每个打开的进程中都可见。

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

正如其他人所说,在Unix中替换共享库的正确方法是使用取消链接或重命名, 而不是用新副本覆盖库。 install命令将正确执行此操作。

如果你能弄清楚你的库被映射到内存中,那么你可能可以对它进行mprotect ,并对每个页面进行简单的写操作(例如读和写每个页面的第一个字节)。 这应该让你每个页面的私人副本。

如果“mprotect”不起作用(可能不是,原始文件可能是以只读方式打开的),那么可以将区域复制到另一个位置,将区域(使用mmap )重新映射到专用可写区域,然后复制该地区。

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

在任何这些场景中,仍然存在一个漏洞窗口 – 在dlopen正在调用初始化程序或者发生重映射调用之前,有人可以修改该库。 你不是很安全,除非你能像@DigitalRoss所描述的那样修复动态链接器。

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

这是一个有趣的问题。 我讨厌在Linux中找到这样的漏洞,并且很想找到解决这些漏洞的方法。

我的建议是受到@Paul Tomblin 对Linux上临时文件的回答。 这里的其他一些答案已经暗示了这种机制的存在,但是并没有描述如你所请求的从客户端应用程序中利用它的方法。

我还没有测试过,所以我不知道它会工作多好。 此外,与创建临时文件和取消关联时间短暂相关的竞争条件可能存在较小的安全性问题。 另外,你已经注意到创建一个图书馆的副本的可能性,这正是我所提出的。 我的麻烦就在于,无论您打开图书馆多长时间,您的临时副本都只是作为文件系统中的一个条目存在。

当您想要加载库时,请按照以下步骤操作:

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

这将是很好,如果有一个非常简单的方法来实现“复制文件”的步骤,而不需要你真正的复制文件。 硬连接想到,但我不认为这将用于这些目的。 如果Linux有一个与link()一样易于使用的copy-on-write机制,但是我不知道这样的设施是理想的。

编辑:@赞恩山猫答案指出,创建动态库的自定义副本,如果它们复制到多个进程可能是浪费。 所以我的建议可能只有在明智地应用的情况下才有意义 – 只有那些有风险的库(可能是所有不包含/ lib或/ usr / lib文件的库的一小部分)。