Por que meu ambiente está cheio de?

7

Eu tenho um processo cujo ambiente é o seguinte:

root@a-vm:/proc/1363# hexdump -C environ
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0000016c

Eu nunca vi nada assim; Espero que environ contenha nul key=value pares terminados, portanto, essa saída viola todos os tipos de asserções. Eu estou olhando para um bug do kernel conhecido, ou há alguma maneira legítima no Unix / Linux para fazer isso? (… E em caso afirmativo, por que? Por que o kernel permite mesmo esse absurdo?)

(no Linux, 3.13.0 / Ubuntu Trusty)

(Eu corri para isso ao tentar descobrir por que este processo não está escrevendo alguma saída temporária para o local correto; é suposto usar um determinado diretório para armazenamento temporário, e é informado desse diretório através da configuração da variável% envTMP; mas eu estou definindo TMP para algo que parece um caminho muito normal, não um monte de nuls, e eu nunca vi um env completamente vazio de qualquer maneira.)

    
por Thanatos 13.12.2016 / 20:01

2 respostas

16

Isso não é um absurdo, existe uma maneira legítima de fazer isso no Linux e suas expectativas são errôneas.

As cadeias de caracteres de argumento e ambiente transmitidas ao código de inicialização de um programa pelo kernel são armazenadas na memória virtual do espaço de aplicativo comum, exatamente como qualquer outro dado do programa; e, assim como qualquer outra variável de dados do programa, elas são modificáveis. É bastante legítimo que os programas os modifiquem.

(Note que isto é do ponto de vista do que o kernel fornece e aplica. O que os padrões para linguagens de programação particulares podem dizer não é necessariamente o mesmo. Mas no que diz respeito ao kernel, é apenas uma área de memória virtual do espaço de aplicação para dados do programa legíveis e graváveis.O kernel não se importa com a linguagem de programação da qual você compilou o seu código de máquina.)

O arquivo /proc/${PID}/environ é apenas uma janela para essa memória virtual do espaço do aplicativo. Em vez de lembrar os dados reais do ambiente do processo, o Linux apenas lembra os endereços iniciais e finais da área de ambiente com a qual iniciou o processo, e o arquivo /proc/${PID}/environ apenas lê o que está nessa memória agora. Você não deve esperar que este arquivo contenha uma lista de strings terminadas em ␀. Essa é uma expectativa errônea.

Não há nenhuma função da biblioteca GNU C para modificar a memória que contém essas cadeias. Mas vários programas têm suas próprias funções para fazer isso.

Por exemplo, considere o OpenSSH. O servidor OpenSSH modifica o que o ps mostra para seu vetor argumento, para ler coisas como sshd: JdeBP [priv] .

O servidor OpenSSH contém código que tenta imitar no Linux o que ele pode fazer com a biblioteca BSD C no OpenBSD. No OpenBSD há uma função de biblioteca BSD C chamada setproctitle() que reescreve o vetor de argumento do processo conforme relatado pelo comando ps . Ele chama sysctl() para passar um novo vetor argumento para o kernel, que ps pode ler com sysctl() . O FreeBSD tem uma função similar.

No Linux, como explicado, o kernel não lembra de argumentos e ambiente reais, apenas os endereços de início e fim das áreas de memória onde inicialmente os colocaram ao iniciar o processo. Portanto, a porta Linux do OpenSSH tem uma função setproctitle() de compatibilidade que substitui a área de memória mencionada anteriormente.

Esta função de compatibilidade calcula o tamanho total da área de ambiente e da área de argumento, e sobrescreve tudo isso com a nova cadeia de argumento. Isso ocorre porque, no caso normal, os programas que chamam setproctitle() desejam gravar em um conjunto mais longo de dados de argumentos do que o processo original. sshd geralmente faz. Por isso, permite que os novos argumentos sobrescrevam a área do ambiente que segue a área de argumento, dando aos programas mais espaço para conjuntos mais longos de cadeias de argumentos.

Importante, ele também preenche a parte não usada da área que não precisou ser sobrescrita, com o tamanho original do argumento total e dados do ambiente, com ␀s.

E o que você está vendo é o resultado exato disso. Se você encontrar um processo do servidor OpenSSH em seu sistema, verá que ele também possui muitos ins em seu /proc/${PID}/environ .

Leitura adicional

por 13.12.2016 / 22:37
2

Isso é totalmente factível escrevendo NUL s no local da memória em que as variáveis de ambiente residem:

#include <stdio.h>
#include <unistd.h>

extern char **environ;

int main(void)
{
    int i;
    char *p = *environ;
    /* hopefully your ENV is longer than this */
    for (i = 0; i < 10; i++) *(p + i) = 0;
    printf("hexdump -C /proc/%d/environ\n", getpid());
    sleep(99999);
}

Se você iniciar um programa com um ambiente vazio, o arquivo environ ficará totalmente vazio:

execle("/bin/sleep", "sleep", "999", (char *)NULL, (char *const) NULL)

Portanto, este caso é algo feito por ou para o processo quando ele está em execução (e há pouco para impedir que isso aconteça a menos que você de alguma forma bloqueie essa memória e setenv(3) chamadas possam ser problemáticas ...).

    
por 13.12.2016 / 22:04