my code is as follows: preload.c, with the following content:
#include
#include
int __attribute__((constructor)) main_ini
the answer from mvds is incorrect!
popen() will spawn child process which inherit the preloaded .so lied in parent process. this child process don't care LD_PRELOAD environment.
(The answer is a pure speculation, and may be is incorrect).
Perhaps, when you fork your process, the context of the loaded libraries persists. So, mylib.so
was loaded when you invoked the main program via LD_PRELOAD
. When you unset the variable and forked, it wasn't loaded again; however it already has been loaded by the parent process. Maybe, you should explicitly unload it after forking.
You may also try to "demote" symbols in mylib.so
. To do this, reopen it via dlopen with flags that place it to the end of the symbol resolution queue:
dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL);
edit: so the problem/question actually was: howcome can't you unset LD_PRELOAD
reliably using a preloaded main_init()
from within bash
.
The reason is that execve
, which is called after you popen
, takes the environment from (probably)
extern char **environ;
which is some global state variable that points to your environment. unsetenv()
normally modifies your environment and will therefore have an effect on the contents of **environ
.
If bash
tries to do something special with the environment (well... would it? being a shell?) then you may be in trouble.
Appearantly, bash
overloads unsetenv()
even before main_init()
. Changing the example code to:
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);
}
shows the problem. In normal runs (running cat
, ls
, etc) I get this version of unsetenv:
unsetenv: 7f4c78fd5290
unsetenv: 7f1127317290
unsetenv: 7f1ab63a2290
however, running bash
or sh
:
unsetenv: 46d170
So, there you have it. bash
has got you fooled ;-)
So just modify the environment in place using your own unsetenv
, acting on **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';
}
}
which can be seen to work in the strace
:
execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0
Q.E.D.