¿Por qué no puedo bloquear mi sistema con una bomba tenedor?

54

Recientemente estuve desenterrando información sobre procesos en GNU / Linux y conocí la infame bomba tenedor:

:(){ : | :& }; :

Teóricamente, se supone que se duplicará infinitamente hasta que el sistema se quede sin recursos ...

Sin embargo, he intentado probar tanto en una distribución CLI Debian como en una distribución GUI Mint , y no parece afectar mucho al sistema. Sí, hay toneladas de procesos que se crean, y después de un tiempo leí en la consola mensajes como:

bash: fork: recurso temporalmente no disponible

bash: fork: retry: no hay procesos secundarios

Pero después de un tiempo, todos los procesos simplemente se anulan y todo vuelve a la normalidad. He leído que el ulimit establece una cantidad máxima de proceso por usuario, pero parece que no puedo llegar muy lejos.

¿Cuáles son las protecciones del sistema contra una bomba tenedor? ¿Por qué no se replica hasta que todo se congela o al menos se retrasa mucho? ¿Hay alguna manera de bloquear realmente un sistema con una bomba tenedor?

Plancton
fuente
2
¿Cuál es su PID máximo configurado actualmente?
dsstorefile1
55
Tenga en cuenta que no "bloqueará" su sistema con una bomba de horquilla ... como usted dijo, agotará los recursos y no podrá generar nuevos procesos, pero el sistema no debería bloquearse
Josh
2
¿Qué pasa si corres en su :(){ :& :; }; :lugar? ¿También terminan siendo asesinados todos eventualmente? ¿Qué hay de :(){ while :& do :& done; }; :?
mtraceur
Tu pregunta tan maravillosa me ha convencido a repensar mi voto anterior de "dejar cerrado". Sin embargo, "I" siempre está en mayúscula en inglés, por favor no lo escriba mal más.
user259412

Respuestas:

85

Probablemente tenga una distribución de Linux que use systemd.

Systemd crea un cgroup para cada usuario, y todos los procesos de un usuario pertenecen al mismo cgroup.

Cgroups es un mecanismo de Linux para establecer límites en los recursos del sistema, como el número máximo de procesos, ciclos de CPU, uso de RAM, etc. Esta es una capa diferente, más moderna, de limitación de recursos que ulimit(que utiliza la getrlimit()llamada al sistema).

Si ejecuta systemctl status user-<uid>.slice(que representa el cgroup del usuario), puede ver el número actual y máximo de tareas (procesos y subprocesos) permitidas dentro de ese cgroup.

$ systemctl status user- $ UID.slice
● user-22001.slice: segmento de usuario de UID 22001
   Cargado: cargado
  Entrada: /usr/lib/systemd/system/user-.slice.d
           └─10-defaults.conf
   Activo: activo desde el lunes 2018-09-10 17:36:35 EEST; Hace 1 semanas 3 días
    Tareas: 17 (límite: 10267)
   Memoria: 616.7M

Por defecto, el número máximo de tareas que systemd permitirá para cada usuario es el 33% del "máximo de todo el sistema" ( sysctl kernel.threads-max); esto generalmente equivale a ~ 10,000 tareas. Si desea cambiar este límite:

  • En systemd v239 y posterior, el valor predeterminado del usuario se establece a través de TasksMax = en:

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

    Para ajustar el límite para un usuario específico (que se aplicará inmediatamente y se almacenará en /etc/systemd/system.control), ejecute:

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

    Los mecanismos habituales para anular la configuración de una unidad (como systemctl edit) también se pueden usar aquí, pero requerirán un reinicio. Por ejemplo, si desea cambiar el límite para cada usuario, puede crear /etc/systemd/system/user-.slice.d/15-limits.conf.

  • En systemd v238 y versiones anteriores, el valor predeterminado del usuario se establece a través de UserTasksMax = in /etc/systemd/logind.conf. Cambiar el valor generalmente requiere un reinicio.

Más información sobre esto:

Hkoof
fuente
55
Y 12288 procesos (menos lo que ya se generó antes de la bomba) que no hacen nada excepto tratar de crear uno nuevo, realmente no impactan en un sistema moderno.
Mástil
13

De todos modos, esto ya no bloqueará los sistemas Linux modernos.

Crea acumulaciones de procesos, pero en realidad no quema tanta CPU a medida que los procesos permanecen inactivos. Usted se queda sin ranuras en la tabla de procesos antes de quedarse sin RAM ahora.

Si no está limitado a cgroup como lo señala Hkoof, la siguiente alteración aún hace que los sistemas se caigan:

:(){ : | :& : | :& }; :
Joshua
fuente
55
Esto realmente depende de lo que consideres 'bloquear' el sistema. Quedarse sin ranuras en la tabla de procesos hará que el sistema se ponga de rodillas en la mayoría de los casos, incluso si no causa pánico en el núcleo por completo.
Austin Hemmelgarn
44
@AustinHemmelgarn: es por eso que los sistemas inteligentes reservan los últimos 4 identificadores de proceso para la raíz.
Joshua
2
¿Por qué los procesos quedarían "inactivos"? Cada proceso bifurcado está en una recursión infinita de crear más procesos. Por lo tanto, pasa mucho tiempo en la sobrecarga de llamadas del sistema ( forkuna y otra vez), y el resto de su tiempo haciendo la llamada a la función (usando cada vez más memoria para cada llamada en la pila de llamadas del shell, presumiblemente).
mtraceur
44
@mtraceur: solo ocurre cuando la bifurcación comienza a fallar.
Joshua
1
Oh, lo retiro. Estaba modelando la lógica de una implementación de bomba de horquilla ligeramente diferente en mi cabeza (como esta:) en :(){ :& :; }; :lugar de la de la pregunta. Realmente no he pensado completamente en el flujo de ejecución del arquetípico como se da.
mtraceur
9

En los años 90, accidentalmente solté uno de estos en mí. Inadvertidamente configuré el bit de ejecución en un archivo fuente C que tenía un comando fork (). Cuando hice doble clic en él, csh intentó ejecutarlo en lugar de abrirlo en un editor como quería.

Incluso entonces, no bloqueó el sistema. Unix es lo suficientemente robusto como para que su cuenta y / o el sistema operativo tengan un límite de proceso. En cambio, lo que sucede es que se vuelve muy lento y es probable que falle todo lo que necesite para iniciar un proceso.

Lo que sucede detrás de escena es que la tabla de procesos se llena con procesos que intentan crear nuevos procesos. Si uno de ellos finaliza (ya sea debido a un error en la bifurcación porque la tabla de procesos está llena o debido a un operador desesperado que intenta restaurar la cordura en su sistema), uno de los otros procesos bifurcará alegremente uno nuevo para llenar el vacío.

La "bomba tenedor" es básicamente un sistema de procesos que se repara a sí mismo involuntariamente en una misión para mantener su mesa de procesos llena. La única forma de detenerlo es matarlos de alguna manera a la vez.

TED
fuente
1
Matarlos a todos a la vez es más fácil de lo que piensas: SIGSTOP a todos ellos primero.
Score_Under
2
@Score_Under - Espero que me perdones si no me apresuro inmediatamente a mi Harris Nighthawk más cercano para ver si eso habría solucionado el problema allí. Estoy pensando que obtener un PID y enviarle la señal antes de que muera por la bifurcación fallida y que otro lo lleve a cabo podría ser un desafío, pero tendría que probarlo.
TED
@TED ​​kill -9 -1 puede ser tu amigo aquí (con el mismo usuario que ejecuta la bomba tenedor; no con root).
Andreas Krey
@AndreasKrey: esa bandera no parece familiar, por lo que dudo que Nighthawk de la década de los 90 la tuviera.
TED
1
@TED: -1no es una bandera. killsolo toma una opción y luego detiene las opciones de análisis. Esto mata la identificación del proceso -1, que es un alias para todos los procesos.
Joshua