有没有办法让dlopen故意失败

时间:2018-06-14 07:12:22

标签: c++ c linux dlopen .so

我希望能够从我的库中控制是否允许加载或不使用异常,这意味着在某些情况下我想要dlopen(" mylib.so")返回NULL,只有在所有条件都正确的情况下才会成功。

很多人都在询问动机,在我的库中我多次使用dlopen,我想确保在加载我的库之前已经加载了所有需要的组件。

请记住,我必须使用标准解决方案,这意味着我无法使用外部插件或执行重写dlopen等操作。

4 个答案:

答案 0 :(得分:3)

这可能是一些XY problem。我们无法猜测你的动机和目标(这些都是真正重要的)。您想要做的是在Linux上不可能

仔细阅读,并多次 dlopen(3) man页面。您会注意到,由于"mylib.so"没有/,因此使用LD_LIBRARY_PATH环境变量专门处理它。这就是我通常使用dlopen的绝对文件路径的原因。参见例如realpath(3)glob(3)wordexp(3)。请注意,没有记录的方法使dlopen失败超出所记录的失败案例,即:

  

成功后,dlopen()dlmopen()会返回NULL 句柄          加载库。出错(无法找到文件,无法读取,    这些是格式错误,或在加载过程中导致错误)    函数返回NULL

然后您可以使用dlerror(3)来了解错误原因。

所以你可以用LD_LIBRARY_PATH玩耍但你不应该。你可能很奇怪(实际上很疯狂),例如在其上使用putenv(3),或在该路径中添加由您的程序管理的某些FUSE文件系统中的目录,或者执行一些LD_PRELOAD trick。但是 你真的不应该做这种疯狂的伎俩

所以合理:以其他方式解决您的实际需求。期望dlopen表现得像记录(通常成功),如果你不想,不要调用它。编码时,使用常识很重要。

请注意rpath(可以在链接时明确设置),并仔细阅读Program Library HowTo和Drepper的How To Write Shared Libraries。另请阅读C++ dlopen minihowto并注意name mangling

请注意,实际上dlopen是Linux上C standard library的一部分,而libcld-linux.so(8)通常是free software(例如GNU {{} 3}}或glibc)。因此,如果您对系统dlopen不满意,原则上可以更改它(但我不建议这样做,因为libc是每个Linux系统的基石。)

您可以考虑(可能不是一个好主意)使用一些ELF解析库(如musl-libclibelf,...)或某些ELF分析程序(如libbfdreadelf(1) ...)在 dlopen之前的共享对象上(但恶意进程或用户可能在分析后但dlopen之前仍然会更改共享库)。您可以自己研究objdump(1)格式并手动进行这样的解析(可能更糟糕的想法)。

如果您正在编写mylib.so库(在Linux上,也许还有其他一些类似的操作系统;但由于elf(5)中未指定,此行为是非标准的),您可能会感兴趣{ {3}} {{}}}和__attribute__((constructor))& __attribute__((visibility))(另见POSIX dlopenfunction attributes)。如果你想从你的 dlopen“拒绝”mylib.so - ed(当满足某些条件时),你可以考虑让一些构造函数测试这些条件并调用{{ 1}}当他们失败时如果您的exit是从其他可以改进的程序加载的插件,您可能只需要使用mylib.so寻找一些初始化函数,在dlsym之后调用它,如果主程序失败则初始化函数失败。 BTW,从这样的构造函数(或一些过时的dlopen函数)抛出一些C ++异常是不明智的,因为_init机制可能会消耗在这种情况下不会释放的内部资源。

最后,从理论上讲,您可以自己重新实施dlopenthis以上,that等...并关注open(2) mmap(2)明确地)。这可能需要几年时间(并且是特定于处理器的)。研究适当的relocations

你可以通过使用通常的dlopen来实现你的(未说明的)目标,并在它之前做一些测试,也许在它之后进行一些测试(使用dlopen)。大多数使用插件的程序都是这样做的。

也许你可能会让dlsym的每个导出函数在运行时都进行适当的检查。也许你可以通过mylib.so的某个函数设置一些静态布尔标志(所以它会在__attribute__((constructor))时调用一次)并让其他公共函数检查该标志。

在最近的编辑中,您最后解释:

  

在我的库中我多次使用dlopen,我想确保在加载我的库之前已经加载了所有需要的组件。

无需使用dlopen或其构造函数(您可能不需要在库中使用dlopen;如果您这样做,则需要解释原因,如何,以及在哪里)。您只需将dlopen与其使用的所有必需共享库相关联(请参阅ELF)。如果它们在mylib.so时无法加载或无法访问,dlopen的整个dlopen都会失败(直观地说,在Linux上,x86 ABI会以某种方式“递归”。)

顺便说一句,如果您确实在mylib.so内拨打了dlopen,那么mylib.so dlopen mylib.so之后发生 < - > ed(除非你从dlopen的某个构造函数调用dlopen,这很奇怪,但应该没问题并且提出不同的问题。)

答案 1 :(得分:2)

据我记得,不,你不能,至少以正常的方式。如果库存在于给定路径,则将加载它。 dlopen dlopen2并非旨在进行任何&#34;业务检查&#34;由您自行决定。最多,它将遵守任何文件系统权限/ etc,如果进程无权访问该文件,则会返回错误。

如果你可以完全控制将加载你的库的代码,那么像Atterson建议的那样包装它并使用dlopen2并完成它。

如果您没有完全控制权,那么dlopen仍然不会阻止任何人使用原始dlopen2并绕过检查。你可以试着让它变得更聪明,例如,让你的dlopen做一些可检测的事情,这样如果它被dlopen2而不是dlopen打开,那么图书馆可以拒绝工作,但随后。有人可以假装那些&#34;可检测的东西&#34;,然后使用dlopen,完成。然后它归结为使那些&#34;可检测的东西&#34;很难被攻击者重现。

简单地说,它不打算做这些事情。如果操作系统允许(〜文件系统权限等),则意味着加载库。

对于任何其他&#34;访问检查&#34;喜欢&#34;你有执照吗?没有?然后走开&#34;你必须在库中实现它。让他们通过initialization加载它,然后让库检查权限,例如,在每次调用它的函数时。使用异常,或只执行任何操作并返回NULL。或者甚至更好,您可能可以使用void函数(请参阅https://stackoverflow.com/a/1602459/717732 + http://tldp.org/HOWTO/Program-Library-HOWTO/miscellaneous.html#INIT-AND-CLEANUP)并在加载lib时执行一次检查。请注意,此函数会返回dlopen,因此仍然无法使extend失败,但至少库可以暂停其功能。

答案 2 :(得分:1)

你总是可以把它包装成一个函数

BenchmarkName-8           500000              2453 ns/op
BenchmarkNotPool-8        200000              7984 ns/op

答案 3 :(得分:1)

处理这种情况的正确方法是使用 seccomp 并限制允许执行dlopen的操作。

http://man7.org/linux/man-pages/man3/seccomp_rule_add.3.html

当然,这种配置需要root权限。