LD_PRELOAD влияет на нового ребенка даже после unsetenv ("LD_PRELOAD") - PullRequest
6 голосов
/ 18 июля 2010

Мой код выглядит следующим образом: 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);
}

затем в оболочке (осторожно выполните 2-ю команду !!):

    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; либо как команда, которую запускает пользователь, либо как bash, который выполняет popen.
  2. дополнительные Ключевые факторы: 1) выполнить popen из предварительно загруженной библиотеки, 2) возможно, потребуется выполнить popen в разделе инициализации библиотеки.
  3. , если вы используете:

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

    вместо последней команды вы получите много файлов ld-debug с именем /tmp/ld-debug.*. Один для каждого раздвоенного процесса. Во всех этих файлах вы увидите, что символы сначала ищутся в mylib.so, даже если LD_PRELOAD был удален из среды.

Ответы [ 3 ]

8 голосов
/ 18 июля 2010

edit: , поэтому проблема / вопрос на самом деле заключались в следующем: почему вы не можете надежно сбросить LD_PRELOAD, используя предварительно загруженный main_init() из bash.

Причина в том, что execve, который вызывается после того, как вы popen, отбирает у окружения (вероятно)

extern char **environ;

, которая является некоторой глобальной переменной состояния, которая указывает на вашу среду. unsetenv() обычно изменяет вашу среду и, следовательно, влияет на содержимое **environ.

Если bash попытается сделать что-то особенное с окружающей средой (ну ... это будет оболочкой?), Тогда у вас могут быть проблемы.

По-видимому, bash перегружает 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);
}

показывает проблему. В обычных условиях (работает cat, ls и т. Д.) Я получаю эту версию unsetenv:

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

однако, работает bash или sh:

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

1048 * что и требовалось доказать *

2 голосов
/ 18 июля 2010

(Ответ - чистое предположение, и может быть неверно).

Возможно, когда вы разветвляете свой процесс, контекст загруженных библиотек сохраняется.Итак, mylib.so было загружено , когда вы вызывали основную программу через LD_PRELOAD.Когда вы сбрасываете переменную и разветвляетесь, она не загружается снова;однако он уже был загружен родительским процессом.Возможно, вам следует явно разгрузить его после разветвления.

Вы также можете попытаться «понизить» символы в mylib.so.Для этого снова откройте его через dlopen с флагами, которые помещают его в конец очереди разрешения символов:

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

0 голосов
/ 29 июня 2016

неверный ответ от mvds!

popen () порождает дочерний процесс, который наследует предварительно загруженный .so, лежащий в родительском процессе. этот дочерний процесс не заботит среда LD_PRELOAD.

...