LD_PRELOAD即使在unsetenv(“LD_PRELOAD”)之后也会影响新的子项

我的代码如下:preload.c,具有以下内容:

#include <stdio.h> #include <stdlib.h> int __attribute__((constructor)) main_init(void) { printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD")); FILE *fp = popen("ls", "r"); pclose(fp); } 

然后在shell(小心地执行第二个命令!!):

  gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC LD_PRELOAD=./mylib.so bash 

要小心上一个命令,会导致无限循环的分叉“sh -c ls”。 2秒后用^ C停止,(或更好的^ Z,然后看ps)。

更多信息

  1. 这个问题在某种程度上与bash有关; 无论是作为用户运行的命令,还是作为popen执行的bash。
  2. 额外的关键因素:1)从预加载的库中执行popen,2)可能需要在库的初始化部分做popen。
  3. 如果你使用:

     LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash 

    而不是最后一个命令,你将会得到许多名为/tmp/ld-debug.*的ld-debug文件。 每个分叉过程一个。 在所有这些文件中,即使LD_PRELOAD已从环境中删除,您仍会看到符号首先在mylib.so中search。

Solutions Collecting From Web of "LD_PRELOAD即使在unsetenv(“LD_PRELOAD”)之后也会影响新的子项"

编辑:所以这个问题/问题实际上是:怎么不能在bash使用预加载的main_init()可靠地取消LD_PRELOAD

原因是,你在popen后被调用的execve从(可能)

 extern char **environ; 

这是一些指向您的环境的全局状态变量。 unsetenv()通常会修改您的环境,因此会影响**environ的内容。

如果bash试图对环境做一些特殊的事情(好吧,那么它会成为一个shell),那么你可能会遇到麻烦。

main_init()bashmain_init()之前重载了unsetenv() main_init() 。 将示例代码更改为:

 extern char**environ; int __attribute__((constructor)) main_init(void) { int i; printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD")); printf("LD_PRELOAD: \"%s\"\n",getenv("LD_PRELOAD")); printf("Environ: %lx\n",environ); printf("unsetenv: %lx\n",unsetenv); for (i=0;environ[i];i++ ) printf("env: %s\n",environ[i]); fflush(stdout); FILE *fp = popen("ls", "r"); pclose(fp); } 

显示问题。 在正常运行(运行catls等)我得到这个版本的unsetenv:

 unsetenv: 7f4c78fd5290 unsetenv: 7f1127317290 unsetenv: 7f1ab63a2290 

然而,运行bashsh

 unsetenv: 46d170 

所以你有它。 bash让你愚弄;-)

所以只要修改环境,使用自己的unsetenv ,作用于**environ

 for (i=0;environ[i];i++ ) { if ( strstr(environ[i],"LD_PRELOAD=") ) { printf("hacking out LD_PRELOAD from environ[%d]\n",i); environ[i][0] = 'D'; } } 

这可以被看作是在strace工作:

 execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0 

QED

(答案是纯粹的猜测, 可能是不正确的)。

也许,当你分叉你的进程时,加载的库的上下文仍然存在。 所以,当您通过LD_PRELOAD调用主程序时, mylib.so 被加载 当你取消设置变量和分叉时,它不会再被加载。 但是它已经父进程加载了。 也许,你应该在分叉之后明确地卸载它。

您也可以尝试在mylib.so “降级”符号。 要做到这一点,通过使用将其放置到符号解析队列末尾的标志通过dlopen重新打开:

 dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL); 

从mvds的答案是不正确的!

popen()会产生继承预加载的.so子进程。 这个子进程不关心LD_PRELOAD环境。