我有一个用C ++编写的程序,使用dlopen加载动态库(Linux、i386、.so)。当库文件随后被修改时,我的程序往往会崩溃。这可以理解,因为文件可能只是映射到内存中。
我的问题是:除了简单地创建文件的副本并dlopen它之外,是否有一种安全的方式来加载共享对象,以防止随后的修改,或者有没有办法从我已经加载的共享对象的修改中恢复过来?
澄清:问题不是“如何安装新库而不使程序崩溃”,而是“如果我无法控制某些人复制库文件,是否有可能对此进行防御?”
rm
命令删除旧库,那么系统会保留已分配的inode、打开的文件以及正在运行的程序。(当程序最终退出时,大多数隐藏但仍存在的文件资源将被释放。)更新:好的,在澄清之后。动态链接器实际上通过向mmap(2)
传递MAP_COPY
标志(如果可用)完全“解决”了这个问题。然而,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攻击,而只是本地的攻击。
如果某人拥有文件写入权限,那么防止其覆盖您的库是不可能的。
由于 dlopen
函数将内存映射到库文件中,因此对文件进行的所有更改都会在打开它的每个进程中可见。
dlopen
函数使用内存映射,因为这是使用共享库最节省内存的方式。私有副本会浪费内存。
正如其他人所说,在Unix中替换共享库的正确方法是使用 unlink 或 rename,而不是直接用新副本覆盖库文件。相应命令install
可以正确地执行此操作。
mprotect
将其设置为可写,并对每个页面进行简单的写入(例如读取并写回每个页面的第一个字节)。这样应该会得到每个页面的私有副本。mmap
)到私有的可写区域,然后将该区域复制回来。