Почему я не могу разбить мою систему с помощью вилочной бомбы?

54

Недавно я копал информацию о процессах в GNU / Linux и встретил печально известную вилочную бомбу:

:(){ : | :& }; :

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

Тем не менее, я пробовал тестировать как на CLI Debian, так и на графическом дистрибутиве Mint , и это, похоже, не сильно влияет на систему. Да, есть тонны процессов, которые создаются, и через некоторое время я читаю в консоли сообщения вроде:

bash: fork: ресурс временно недоступен

bash: fork: retry: нет дочерних процессов

Но через некоторое время все процессы просто убиваются, и все возвращается к нормальной жизни. Я читал, что ulimit устанавливает максимальное количество процессов для каждого пользователя, но я не могу поднять его действительно далеко.

Какие системы защиты от вилочной бомбы? Почему он не копирует себя, пока все не замерзнет или, по крайней мере, не сильно отстает? Есть ли способ действительно разбить систему с помощью вилочной бомбы?

Plancton
источник
2
Какой максимальный PID установлен в данный момент?
dsstorefile1
5
Обратите внимание, что вы не «сломаете» свою систему с помощью вилочной бомбы ... как вы сказали, вы исчерпаете ресурсы и не сможете создавать новые процессы, но система не должна аварийно
Josh
2
Что произойдет, если вы запустите :(){ :& :; }; :вместо этого? Они тоже все в конечном итоге погибают? Как насчет :(){ while :& do :& done; }; :?
mtraceur
Ваш этот замечательный вопрос убедил меня переосмыслить мое предыдущее "закрытое голосование". Тем не менее, «Я» всегда пишется в верхнем регистре на английском языке, пожалуйста, не пишите это плохо.
user259412

Ответы:

85

Возможно, у вас есть дистрибутив Linux, использующий systemd.

Systemd создает группу для каждого пользователя, и все процессы пользователя принадлежат одной и той же группе.

Cgroups - это механизм Linux, устанавливающий ограничения на системные ресурсы, такие как максимальное количество процессов, циклы ЦП, использование ОЗУ и т. Д. Это другой, более современный уровень ограничения ресурсов, чем ulimit(который использует getrlimit()системный вызов).

Если вы запустите systemctl status user-<uid>.slice(который представляет cgroup пользователя), вы увидите текущее и максимальное количество задач (процессов и потоков), которые разрешены в этой cgroup.

$ systemctl status user- $ UID.slice
● user-22001.slice - пользовательский срез UID 22001
   Загружен: загружен
  Drop-In: /usr/lib/systemd/system/user-.slice.d
           └─10-defaults.conf
   Активен: активен с понедельника 2018-09-10 17:36:35 EEST; 1 неделя 3 дня назад
    Задачи: 17 (лимит: 10267)
   Память: 616,7M

По умолчанию максимальное количество задач, которое systemd разрешает для каждого пользователя, составляет 33% от «общесистемного максимума» ( sysctl kernel.threads-max); обычно это составляет около 10000 задач. Если вы хотите изменить это ограничение:

  • В systemd v239 и более поздних версиях пользователь по умолчанию устанавливается через TasksMax = in:

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

    Чтобы настроить ограничение для конкретного пользователя (которое будет применено сразу же, а также сохранено в /etc/systemd/system.control), выполните:

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

    Здесь также можно использовать обычные механизмы переопределения настроек устройства (например, systemctl edit), но они потребуют перезагрузки. Например, если вы хотите изменить ограничение для каждого пользователя, вы можете создать /etc/systemd/system/user-.slice.d/15-limits.conf.

  • В systemd v238 и более ранних версиях пользователь по умолчанию устанавливается через UserTasksMax = in /etc/systemd/logind.conf. Изменение значения обычно требует перезагрузки.

Подробнее об этом:

Hkoof
источник
5
И 12288 процессов (за исключением того, что уже было создано до взрыва бомбы) ничего не делают, кроме попыток создать новый, на самом деле не влияют на современную систему.
Мачта
13

Это больше не приведет к краху современных систем Linux.

Он создает запасы процессов, но на самом деле не сжигает столько ЦП, поскольку процессы простаивают. Вы исчерпали слоты в таблице процессов, прежде чем исчерпали оперативную память.

Если вы не ограничены в cgroup, как указывает Hkoof, следующее изменение по-прежнему разрушает системы:

:(){ : | :& : | :& }; :
Джошуа
источник
5
Это действительно зависит от того, что вы считаете «крахом» системы. Недостаток слотов в таблице процессов в большинстве случаев ставит систему на колени, даже если она не вызывает паники ядра.
Остин Хеммельгарн
4
@AustinHemmelgarn: Вот почему мудрые системы резервируют последние 4 или около того идентификатора процесса для root.
Джошуа
2
Почему процессы идут "вхолостую"? Каждый разветвленный процесс находится в бесконечной рекурсии создания большего количества процессов. Таким образом, он тратит много времени на системные вызовы ( forkснова и снова), а оставшуюся часть времени выполняет вызов функции (постепенно увеличивая использование памяти для каждого вызова в стеке вызовов оболочки, предположительно).
mtraceur
4
@mtraceur: это происходит только тогда, когда начинает разваливаться вилка.
Джошуа
1
О, я забираю это обратно. Я моделировал логику реализации немного другой бомбы-вилки в моей голове (как это :) :(){ :& :; }; :вместо той, о которой идет речь. Я на самом деле не полностью продумал поток выполнения архетипического, как дано.
mtraceur
9

Еще в 90-х я случайно спустил одну из них на себя. Я случайно установил бит выполнения в исходном файле C, в котором была команда fork (). Когда я дважды щелкнул по нему, csh попытался запустить его, а не открыть его в редакторе, как я хотел.

Даже тогда это не сломало систему. Unix достаточно устойчив, чтобы ваша учетная запись и / или ОС имели ограничение по процессу. Вместо этого все становится очень вялым, и все, что нужно для запуска процесса, может потерпеть неудачу.

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

«Вилочная бомба» - это, по сути, непреднамеренно самовосстанавливающаяся система процессов, задача которых - сохранить таблицу процессов заполненной. Единственный способ остановить это - убить их всех сразу.

ТЕД
источник
1
Убить их всех сразу легче, чем вы думаете - сначала SIGSTOP их всех.
Score_Und
2
@Score_Under - Надеюсь, вы простите меня, если я не сразу уйду к ближайшему Харрису Ночному Ястребу, чтобы посмотреть, не устранит ли это проблему. Я думаю, просто получить PID, послав ему сигнал до того, как он умрет от неисправной вилки, а другой займет его место, может быть проблемой, но я должен был бы попробовать это.
TED
@TED ​​kill -9 -1 может быть вашим другом здесь (с тем же пользователем, который запускает вилочную бомбу; не с root).
Андреас Крей
@AndreasKrey - Этот флаг не выглядит знакомым, поэтому я сомневаюсь, что он был в «90-х годах».
TED
1
@TED: -1не флаг. killпринимает только одну опцию, затем останавливает парсинг опций. Это убивает идентификатор процесса -1, который является псевдонимом для всех процессов.
Джошуа