| cano

| cano

Entendo como funciona uma fork bomb normal, mas realmente não entendo por que o & no final da fork bomb comum do bash é necessário e por que esses scripts se comportam de maneira diferente:

:(){ (:) | (:) }; :

e

:(){ : | :& }; :

O primeiro causa um pico de uso da CPU antes de me levar de volta à tela de login. O último, em vez disso, apenas faz com que meu sistema congele, forçando-me a uma reinicialização forçada. Por que é que? Ambos criam continuamente novos processos, então por que o sistema se comporta de maneira diferente?

Ambos os scripts também se comportam de maneira diferente de

:(){ : | : }; :

o que não causa nenhum problema, embora eu esperasse que fossem iguais. A página de manual do bash afirma que os comandos em um pipeline já são executados em um subshell, então sou levado a acreditar que: | : já deve ser suficiente. Eu acredito e deveria apenas executar o pipeline em um novo subshell, mas por que isso muda tanto?

Editar: Usando htop e limitando a quantidade de processos, pude ver que a primeira variante cria uma árvore real de processos, a segunda variante cria todos os processos no mesmo nível e a última variante não parece criar nenhum processo de forma alguma. Isso me confunde ainda mais, mas talvez ajude de alguma forma?

Responder1

AVISO NÃO TENTE OPERAR ISTO EM UMA MÁQUINA DE PRODUÇÃO. SÓ NÃO. Aviso: Para testar qualquer "bomba" certifique-se ulimit -ude que esteja em uso. Leia abaixo [a] .

Vamos definir uma função para obter o PID e a data (hora):

bize:~$ d(){ printf '%7s %07d %s\n' "$1" "$BASHPID" "$(date +'%H:%M:%S')"; }

Uma função simples e sem problemas bombpara o novo usuário (proteja-se: leia [a] ):

bize:~$ bomb() { d START; echo "yes"; sleep 1; d END; } >&2

Quando essa função é chamada para ser executada funciona assim:

bize:~$ bomb
  START 0002786 23:07:34
yes
    END 0002786 23:07:35
bize:~$

O comando dateé executado, em seguida é impresso um "sim", um sleep por 1 segundo, depois o comando de fechamento datee, por fim, a função sai imprimindo um novo prompt de comando. Nada chique.

| cano

Quando chamamos a função assim:

bize:~$ bomb | bomb
  START 0003365 23:11:34
yes
  START 0003366 23:11:34
yes
    END 0003365 23:11:35
    END 0003366 23:11:35
bize:~$

Dois comandos são iniciados ao mesmo tempo, ambos os comandos terminarão 1 segundo depois eentãoo prompt retorna.

Essa é a razão do pipe |iniciar dois processos em paralelo.

& fundo

Se mudarmos a chamada adicionando uma finalização &:

bize:~$ bomb | bomb &
[1] 3380
bize:~$
  START 0003379 23:14:14
yes
  START 0003380 23:14:14
yes
    END 0003379 23:14:15
    END 0003380 23:14:15

O prompt retorna imediatamente (toda a ação é enviada para segundo plano) e os dois comandos são executados como antes. Observe o valor do "número do trabalho" [1]impresso antes do PID do processo 3380. Posteriormente, o mesmo número será impresso para indicar que o pipe terminou:

[1]+  Done                    bomb | bomb

Esse é o efeito de &.

Essa é a razão de &: iniciar os processos com mais rapidez.

Nome mais simples

Podemos criar uma função chamada simplesmente bpara executar os dois comandos. Digitado em três linhas:

bize:~$ b(){
> bomb | bomb
> }

E executado como:

bize:~$ b
  START 0003563 23:21:10
yes
  START 0003564 23:21:10
yes
    END 0003564 23:21:11
    END 0003563 23:21:11

Observe que usamos no ;na definição de b(as novas linhas foram usadas para separar elementos). Porém, para uma definição em uma linha, é comum usar ;, assim:

bize:~$ b(){ bomb | bomb ; }

A maioria dos espaços também não são obrigatórios, podemos escrever o equivalente (mas menos claro):

bize:~$ b(){ bomb|bomb;}

Também podemos usar a &para separar }(e enviar os dois processos para segundo plano).

A bomba.

Se fizermos a função morder o rabo (chamando a si mesma), obteremos a "fork bomb":

bize:~$ b(){ b|b;}       ### May look better as b(){ b | b ; } but does the same.

E para que ele chame mais funções com mais rapidez, envie o pipe para segundo plano.

bize:~$ b(){ b|b&}       ### Usually written as b(){ b|b& }

Se anexarmos a primeira chamada à função após um require ;e alterarmos o nome para, :obteremos:

bize:~$ :(){ :|:&};:

Geralmente escrito como:(){ :|:& }; :

Ou, escrito de forma divertida, com algum outro nome (um boneco de neve):

☃(){ ☃|☃&};☃

O ulimit (que você deveria ter definido antes de executar isso) fará com que o prompt retorne rapidamente após muitos erros (pressione enter quando a lista de erros parar para obter o prompt).

A razão pela qual isso é chamado de "fork bomb" é que a maneira pela qual o shell inicia um sub-shell é bifurcando o shell em execução e depois chamando exec() para o processo bifurcado com o comando a ser executado.

Um tubo “bifurcará” dois novos processos. Fazer isso até o infinito causa uma bomba.
Ou um coelho, como foi originalmente chamado porque se reproduz muito rapidamente.


Tempo:

  1. :(){ (:) | (:) }; time :
    Terminou
    reais 0m45.627s

  2. :(){ : | :; }; time :
    Terminou
    reais 0m15.283s

  3. :(){ : | :& }; time :
    real 0m00.002 s
    ainda em execução


Seus exemplos:

  1. :(){ (:) | (:) }; :

    Onde o segundo fechamento )separa o }é uma versão mais complexa de :(){ :|:;};:. De qualquer maneira, cada comando em um pipe é chamado dentro de um subshell. Qual é o efeito do ().

  2. :(){ : | :& }; :

    É a versão mais rápida, escrita sem espaços: :(){(:)|:&};:(13 caracteres).

  3. :(){ : | : }; : ### funciona em zsh, mas não em bash.

    Tem um erro de sintaxe (no bash), é necessário um metacaractere antes do fechamento },
    como este:

     :(){ : | :; }; :
    

[a] Crie um novo usuário limpo (chamarei de meu bize). Faça login neste novo usuário em um console sudo -i -u bizeou:

$ su - bize
Password: 
bize:~$

Verifique e altere o max user processeslimite:

bize:~$ ulimit -a           ### List all limits (I show only `-u`)
max user processes              (-u) 63931
bize:~$ ulimit -u 10        ### Low
bize:~$ ulimit -a
max user processes              (-u) 1000

Usar apenas 10 funciona, assim como apenas um novo usuário solitário: bize. Torna mais fácil ligar killall -u bizee livrar o sistema da maioria (não de todas) das bombas. Por favor, não pergunte quais ainda funcionam, não direi. Mas ainda:É bastante baixo, mas pelo lado seguro, adapte-se ao seu sistema.
Esseirá garantir que uma "bomba fork" não irá colapsar seu sistema.

Leitura adicional:

informação relacionada