LD_PRELOAD affecte un nouvel enfant, même après une désinstallation (“LD_PRELOAD”)

mon code est le suivant: preload.c, avec le contenu suivant:

#include  #include  int __atsortingbute__((constructor)) main_init(void) { printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD")); FILE *fp = popen("ls", "r"); pclose(fp); } 

puis dans la shell (faites la 2ème commande avec soin !!):

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

!!! attention avec la dernière commande il en résultera une boucle sans fin de “sh -c ls”. Arrêtez-le après 2 secondes avec ^ C, (ou mieux ^ Z et voyez ensuite ps).

Plus d’informations

  1. Ce problème concerne la bash en quelque sorte; soit en tant que commande exécutée par l’utilisateur, soit en tant que bash exécutée par le popen.
  2. Les facteurs clés supplémentaires: 1) exécuter le popen à partir de la bibliothèque pré-chargée, 2) probablement faire le popen dans la section d’initialisation de la bibliothèque.
  3. si tu utilises:

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

    Au lieu de la dernière commande, vous obtiendrez de nombreux fichiers ld-debug, nommés /tmp/ld-debug.*. Un pour chaque processus fourchu. Dans tous ces fichiers, vous verrez que les symboles sont d’abord recherchés dans mylib.so même si LD_PRELOAD a été supprimé de l’environnement.

edit: le problème / la question était donc le suivant: comment pouvez-vous désinstaller LD_PRELOAD manière fiable en utilisant main_init() préchargé depuis bash ?

La raison est que execve , qui est appelé après que vous ayez popen , prenne l’environnement (probablement)

 extern char **environ; 

qui est une variable d’état globale qui pointe vers votre environnement. unsetenv() modifie normalement votre environnement et aura donc un effet sur le contenu de **environ .

Si bash essaie de faire quelque chose de spécial avec l’environnement (eh bien … serait-ce un shell?) Alors vous pourriez avoir des problèmes.

unsetenv() , bash surcharge unsetenv() avant même main_init() . Changer l’exemple de code pour:

 extern char**environ; int __atsortingbute__((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); } 

montre le problème. Dans les courses normales ( cat courant, ls , etc.) je reçois cette version de unsetenv:

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

Cependant, en exécutant bash ou sh :

 unsetenv: 46d170 

Alors, voilà. bash vous a dupé 😉

Il suffit donc de modifier l’environnement en place en utilisant votre propre unsetenv , en agissant sur **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'; } } 

qui peut être vu pour travailler dans le strace :

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

QED

(La réponse est une pure spéculation et peut être incorrecte).

Peut-être que lorsque vous décomposez votre processus, le contexte des bibliothèques chargées persiste. Ainsi, mylib.so été chargé lorsque vous avez mylib.so le programme principal via LD_PRELOAD . Lorsque vous avez désélectionné la variable et que vous avez créé une fourchette, celle-ci n’a plus été chargée. Cependant, il a déjà été chargé par le processus parent. Peut-être devriez-vous le décharger explicitement après avoir bifurqué.

Vous pouvez également essayer de “rétrograder” les symboles dans mylib.so . Pour ce faire, rouvrez-le via dlopen avec les indicateurs qui le placent à la fin de la queue de résolution des symboles:

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

la réponse de mvds est incorrecte!

popen () va générer un processus enfant qui hérite du fichier .so préchargé dans le processus parent. ce processus enfant ne se soucie pas de l’environnement LD_PRELOAD.