Что происходит с памятью, когда мы используем putenv() для переменной окружения, которая уже существует?

Чтобы запустить unshare, у вас должны быть права суперпользователя, чтобы создать отдельное пространство для монтирования.

Я попробовал это, похоже, оно делает то, что вы хотите (Я думаю):

Ishtar:> mkdir -p /tmp/unshare/home
Ishtar:> cd /tmp/unshare
Ishtar:/tmp/unshare> sudo unshare -m /bin/bash
Ishtar:/tmp/unshare# mount --rbind /home/packages /tmp/unshare/home
Ishtar:/tmp/unshare# tty
/dev/pts/4
Ishtar:/tmp/unshare# # ls home
BUILD@      RPMS@     build/           linux@    sources/           tmp/
BUILDROOT@  SOURCES@  buildroot/       logs/     specs/
OSbuild/    SPECS@    config-scripts/  perlsrc/  srpms/
OTHER/      SRPMS@    debug@           rpms/     sysvinit-288.spec

Таким образом, описанный выше процесс смонтировал '/home/packages @ /tmp/unshare/home.

В другом окне tty -с любым пользователем я могу попытаться увидеть :что находится в /tmp/unshare/home:

Ishtar:/> tty
/dev/pts/5
Ishtar:/> ll /tmp/unshare/home
total 0
Ishtar:/> cd tmp/unshare
Ishtar:/tmp/unshare> sudo
Ishtar:/tmp/unshare# ls home
Ishtar:/tmp/unshare# ll home
total 0
# create file in original "bound" dir from 1st usr above:
Ishtar:/tmp/unshare# touch /home/packages/PACKAGES.DIR 
Ishtar:/tmp/unshare# ll home  #home still empty
total 0
Ishtar:/> tty
/dev/pts/5
# now on other user again
Ishtar:/tmp/unshare# tty
/dev/pts/4
Ishtar:/tmp/unshare# ls home
BUILD@        RPMS@     buildroot/       perlsrc/  sysvinit-288.spec
BUILDROOT@    SOURCES@  config-scripts/  rpms/     tmp/
OSbuild/      SPECS@    debug@           sources/
OTHER/        SRPMS@    linux@           specs/
PACKAGES.DIR  build/    logs/            srpms/
#^^^ see PACKAGES.DIR appear (as created in original dir by another
# user

Как только вы смонтируете свой «личный каталог для пользователя в «pts/4», вы можете измените на UID, под которым вы хотите запустить программу:

Ishtar:/tmp/unshare# su astara
Ishtar:/tmp/unshare> whoami
astara
Ishtar:/tmp/unshare> ls home/PACK*
home/PACKAGES.DIR

Обратите внимание, что mount по-прежнему доступен для непривилегированного пользователя.

Чтобы спастись,Я бы поместил «su другому пользователю» в файл сценария, за которым следовал с помощью 'unmount /tmp/unshare/home', (, так как при выходе su OTHERUSER он снова станет root и размонтирует файл в личном пространстве ). Затем вы можете выйти.

Это близко к тому, что вы хотите? --Для установки необходимо использовать root ваш дочерний env, но затем запустите дочерний --и только он имеет доступ к монтирование, созданное в новом пространстве имен монтирования.

(update )BTW --только что заметил, что unshare имеет --карту -пользователя root -, который специально разрешает использовать либо root, либо заглавные буквы для настройки параметров в новом пространстве имен. На странице руководства (говорится об этом переключателе ):

.
....This makes it possible to  conveniently
gain  capabilities needed to manage various aspects of the newly
created namespaces (such as configuring interfaces in  the  net-
work  namespace  or mounting filesystems in the mount namespace)
even when run unprivileged.

Это может позволить вам управлять разработчиком цикла, не будучи пользователем root (или так говорится на странице руководства ). Вероятнее всего, CAP _SYS _ADMIN — это ограничение, необходимое для этого.

1
14.08.2019, 19:15
3 ответа

Ваше наблюдение верно. Новая строка, которую вы выделили с помощью malloc, заменяет старую строку, и старая строка больше не используется. Если старая строка является частью среды, переданной при запуске программы (, т. е. это первый раз, когда переменная изменяется в этом процессе ), то старая строка является частью сегмента стека.

Является ли это утечкой памяти или нет, спорный вопрос. Вы не можете освободить память, занятую старой строкой, но и нет разумного способа повторно использовать эту память. Если вы вызовете putenvнесколько раз, вы, конечно, можете freeвыделить память, которую вы выделили себе, когда строки больше не используются.

Обратите внимание, что семантика putenvзависит от версии glibc, как описано на странице руководства .

Кстати, в вашей программе ошибка, а не

strcat(temp, "");
strcat(temp, "PWD=/home/mycomputer/");

вы должны написать

strcpy(temp, "PWD=/home/mycomputer/");
2
27.01.2020, 23:17

Жалко, что вы переработали свой вопрос из первой версии, которая касалась setenv(), потому чтоsetenv()имеет утечку памяти, по крайней мере, в реализации glibc.

Возьмите этот пример программы; он будет расти и расти, пока не съест всю память, которую сможет:

#include <stdlib.h>
#include <stdio.h>
int main(void){
        char buf[24];
        for(;;){
                snprintf(buf, sizeof buf, "%d", rand());
                setenv("foo", buf, 1);
        }
}

В реализации glibc setenv()никогда не будет освобождаться память, уже выделенная предыдущим setenv(); однако он будет избегать дублирования, отслеживая строки среды, выделенные им в двоичном дереве (с использованием tfind(3), отдельно от списка char **environ). Это также дает хороший эффект сокрытия утечки от таких инструментов, как valgrind;-)


Что касается putenv(), он полагается на то, что вызывающий объект управляет строкой, переданной ему в качестве аргумента. Таким образом, задача вызывающей стороны состоит в том, чтобы убедиться, что она не пропустит его при повторном вызове putenv(), что он не освободит его, пока он все еще является частью среды, и что он не использует для этого автоматическую/стековую переменную.

Кроме того, изменение строки после вызова putenv()может привести к удалению переменной среды или добавлению другой. Пример:

#define _XOPEN_SOURCE   500
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <unistd.h>
int main(void){
        extern char **environ;
        static char *my_environ[] = { "YUCK=yumm", 0 };
        static char str[256] = "FOO=bar";
        environ = my_environ;
        putenv(str);
        snprintf(str, sizeof str, "BAZ=quux");
        execl("/usr/bin/printenv", "printenv", (void*)0);
        err(1, "execlp");
}

Это требуется по стандарту, но не работало в старых версиях glibc, где putenv()делал копию своего аргумента.


Наконец, и setenv(), и putenv()будут перемещать массив char **environпри добавлении новой переменной в среду.Реализация сохранит свою собственную копию указателя и увеличит ее с помощью realloc(), но не будет возиться со своим исходным (, который указывает на статическую память ), или с памятью, выделенной вручную, например. environ = calloc(sizeof *environ, envlen).

1
27.01.2020, 23:17

Окружающая среда состоит из трех вещей.

  1. Двойной указатель, скажем, envp, называемый environment list pointer.

  2. Это указывает на environment list, который вы можете представить как массив указателей, указывающих на строки окружения.

  3. environment strings, которые имеют форму name=value.

Теперь, где это хранится в макете виртуальной машины процесса?

Ответ: -над стеком; мы можем назвать его старшим адресом .

Он имеет бетонный потолок и бетонное дно ; так как выше нельзя расширить, потому что это потолок планировки; ниже вы не можете расширяться из-за наличия стека.

Теперь о вашем вопросе.--

Когда вы изменяете строку на любое значение, превышающее длину, которую может вместить верхний сегмент, она внутренне, по существу, выполняет malloc.

Затем указатель в списке сред изменяется, чтобы указывать на эту распределенную память. Итак, у вас будет список окружения, в котором некоторые указатели по-прежнему указывают на адреса в верхней памяти, в то время как этот конкретный указатель будет указывать на область в куче . В этом конкретном случае модификации параметра весь список окружения не копируется в кучу.

Что происходит с предыдущей памятью, на которую указывал указатель? Что ж, это выглядит как утечка памяти, но обычно утечкой не является.поскольку эта память уже была там и теперь является бесполезным куском, на который больше нельзя указать.

Я бы не назвал это утечкой памяти, потому что это было не динамическое выделение, а статическое, которое уже было; часть зарезервированного сегмента.

Утечка памяти звучит как некоторая часть памяти, которую вы могли бы использовать для чего-то другого, но не можете, так как нет способа добраться до нее. Но этот кусок памяти по старшему адресу в любом случае был недоступен вам ни для чего другого.

Итак, похоже на утечку памяти; но я бы не назвал это типично так!

Извините за путаницу, я старался быть как можно более ясным:)

2
27.01.2020, 23:17

Теги

Похожие вопросы