Por que não consigo travar meu sistema com uma bomba de garfo?

54

Recentemente, eu tenho desenterrado informações sobre processos no GNU / Linux e conheci a infame bomba de forquilha:

:(){ : | :& }; :

Teoricamente, é suposto duplicar-se infinitamente até o sistema ficar sem recursos ...

No entanto, tentei testar tanto em uma distro CLI Debian quanto em uma GUI Mint , e isso não parece impactar muito o sistema. Sim, existem muitos processos criados e, depois de um tempo, leio mensagens no console, como:

bash: fork: recurso temporariamente indisponível

bash: fork: nova tentativa: nenhum processo filho

Mas depois de algum tempo, todos os processos acabam sendo mortos e tudo volta ao normal. Eu li que o ulimit define uma quantidade máxima de processo por usuário, mas não consigo elevar isso muito longe.

Quais são as proteções do sistema contra uma bomba bifurcada? Por que ele não se reproduz até que tudo congele ou pelo menos fique muito tempo? Existe uma maneira de realmente travar um sistema com uma bomba de garfo?

Plancton
fonte
2
Qual é o seu PID máximo definido atualmente?
precisa saber é o seguinte
5
Observe que você não "travará" seu sistema usando uma bomba de forquilha ... como você disse, esgotará recursos e não poderá gerar novos processos, mas o sistema não deve travar
Josh
2
O que acontece se você executar :(){ :& :; }; :? Eles também acabam sendo mortos? Que tal :(){ while :& do :& done; }; :?
Mtraceur 19/09/19
Sua pergunta maravilhosa me convenceu a repensar meu voto anterior de "deixar fechado". No entanto, "I" está sempre em maiúsculas em inglês; não escreva mais mal.
user259412 29/04

Respostas:

85

Você provavelmente tem uma distribuição Linux que usa systemd.

O Systemd cria um cgroup para cada usuário e todos os processos de um usuário pertencem ao mesmo cgroup.

O Cgroups é um mecanismo do Linux para definir limites de recursos do sistema, como número máximo de processos, ciclos de CPU, uso de RAM, etc. Essa é uma camada diferente e mais moderna de limitação de recursos do que ulimit(que usa o getrlimit()syscall).

Se você executar systemctl status user-<uid>.slice(que representa o cgroup do usuário), poderá ver o número atual e máximo de tarefas (processos e encadeamentos) permitidas nesse cgroup.

$ systemctl status user- $ UID.slice
● user-22001.slice - fatia do usuário do UID 22001
   Carregado: carregado
  Drop-In: /usr/lib/systemd/system/user-.slice.d
           └─10-defaults.conf
   Ativo: ativo desde segunda-feira 2018-09-10 17:36:35 EEST; 1 semanas 3 dias atrás
    Tarefas: 17 (limite: 10267)
   Memória: 616.7M

Por padrão, o número máximo de tarefas que o systemd permitirá para cada usuário é 33% do "máximo do sistema" ( sysctl kernel.threads-max); isso geralmente equivale a ~ 10.000 tarefas. Se você deseja alterar este limite:

  • No systemd v239 e posterior, o padrão do usuário é definido via TasksMax = in:

    /usr/lib/systemd/system/user-.slice.d/10-defaults.conf
    

    Para ajustar o limite para um usuário específico (que será aplicado imediatamente e armazenado em /etc/systemd/system.control), execute:

    systemctl [--runtime] set-property user-<uid>.slice TasksMax=<value>
    

    Os mecanismos habituais de substituir as configurações de uma unidade (como systemctl edit) também podem ser usados ​​aqui, mas exigirão uma reinicialização. Por exemplo, se você quiser alterar o limite para cada usuário, poderá criar /etc/systemd/system/user-.slice.d/15-limits.conf.

  • No systemd v238 e versões anteriores, o padrão do usuário é definido via UserTasksMax = in /etc/systemd/logind.conf. Alterar o valor geralmente requer uma reinicialização.

Mais informações sobre isso:

Hkoof
fonte
5
E 12288 processos (menos o que já foi gerado antes da bomba) sem fazer nada, exceto tentar criar um novo, realmente não afetam um sistema moderno.
mastro
13

Isso não trava mais os modernos sistemas Linux de qualquer maneira.

Ele cria hordas de processos, mas na verdade não queima tanta CPU quanto os processos ficam ociosos. Você fica sem slots na tabela de processos antes de ficar sem RAM agora.

Se você não é um cgroup limitado, como Hkoof aponta, a seguinte alteração ainda reduz os sistemas:

:(){ : | :& : | :& }; :
Joshua
fonte
5
Isso realmente depende do que você considera 'travar' o sistema. A falta de slots na tabela de processos trará um sistema de joelhos na maioria dos casos, mesmo que não cause completamente um pânico no kernel.
Austin Hemmelgarn
4
@ AustinHemmelgarn: É por isso que os sistemas sábios reservam os últimos 4 ou mais IDs de processo para o root.
Joshua
2
Por que os processos ficam "inativos"? Cada processo bifurcado está em uma recursão infinita de criação de mais processos. Portanto, ele passa muito tempo na sobrecarga de chamadas do sistema ( forkrepetidamente) e o resto do tempo executando a chamada de função (usando incrementalmente mais memória para cada chamada na pilha de chamadas do shell, presumivelmente).
Mtraceur # 19/18
4
@ mtraceur: Isso só acontece quando a bifurcação começa a falhar.
Joshua
11
Oh, eu retiro. Eu estava modelando a lógica de uma implementação de fork fork ligeiramente diferente na minha cabeça (assim:) em :(){ :& :; }; :vez da que está em questão. Na verdade, ainda não pensei completamente no fluxo de execução do arquetípico, como dado.
Mtraceur # 19/18
9

Nos anos 90, acidentalmente soltei um desses em mim. Eu inadvertidamente defini o bit de execução em um arquivo de origem C que continha um comando fork (). Quando cliquei duas vezes, o csh tentou executá-lo em vez de abri-lo em um editor como eu queria.

Mesmo assim, não travou o sistema. O Unix é robusto o suficiente para que sua conta e / ou o sistema operacional tenha um limite de processo. O que acontece é que fica super lento, e qualquer coisa que precise iniciar um processo provavelmente falhará.

O que está acontecendo nos bastidores é que a tabela de processos se enche de processos que estão tentando criar novos processos. Se um deles terminar (devido a um erro na bifurcação porque a tabela de processos está cheia ou devido a um operador desesperado tentando restaurar a sanidade de seu sistema), um dos outros processos alegremente bifurcará um novo para preencher o vazio.

A "bomba dos garfos" é basicamente um sistema de processos auto-reparado não intencionalmente, em uma missão para manter sua tabela de processos cheia. A única maneira de impedir isso é matá-los todos de uma vez.

TED
fonte
11
Matá-los todos de uma vez é mais fácil do que você pensa - SIGSTOP todos eles primeiro.
Score_Under
2
@Score_Under - Espero que você me perdoe se eu não for imediatamente para o Harris Nighthawk mais próximo para ver se isso teria resolvido o problema lá. Eu estou pensando que apenas obter um PID e enviá-lo o sinal antes que ele morra do garfo com falha e outro acontecer, pode ser um desafio, mas eu teria que tentar.
TED
@TED ​​kill -9 -1 pode ser seu amigo aqui (com o mesmo usuário que executa o fork bomb; não com o root).
Andreas Krey
@AndreasKrey - Essa bandeira não parece familiar, então eu estou duvidando que o Nighthawk da minha década de 90 os tenha.
TED
11
@TED: -1não é uma bandeira. killleva apenas uma opção e para de analisar as opções. Isso mata o ID do processo -1, que é um alias para todos os processos.
Joshua