vulnerability seccion race ejemplo critica condiciones condicion competencia carrera multithreading concurrency race-condition

multithreading - seccion - ¿Podemos tener condiciones de carrera en un programa de un solo hilo?



race condition vulnerability (1)

Puede encontrar aquí una muy buena explicación sobre lo que es una condición de carrera.

Recientemente, he visto a mucha gente haciendo afirmaciones confusas sobre las condiciones e hilos de la carrera.

Aprendí que las condiciones de carrera solo pueden ocurrir entre hilos. Pero vi un código que parecía condiciones de carrera, en el caso de los lenguajes asíncronos y basados ​​en eventos, incluso si el programa era de un solo hilo, como en Node.js, en GTK +, etc.

¿Podemos tener una condición de carrera en un solo programa de hilo?


Todos los ejemplos están en un lenguaje ficticio muy cercano a Javascript.

Corto:

  1. Una condición de carrera solo puede ocurrir entre dos o más hilos. No podemos tener condiciones de carrera dentro de un proceso de subproceso único (por ejemplo, en un solo subproceso, programa que no hace E / S).

  2. Pero un solo programa de subprocesos puede en muchos casos:

    1. ofrecen situaciones que se parecen a las condiciones de carrera, como en un programa basado en eventos con un ciclo de eventos, pero no son condiciones reales de carrera

    2. desencadenar una condición de carrera entre o con otro (s) hilo (s), por ejemplo:

      1. otros programas, como clientes
      2. hilos de biblioteca o servidores

I) Las condiciones de carrera solo pueden ocurrir entre dos o más hilos

Una condición de carrera solo puede ocurrir cuando dos o más hilos intentan acceder a un recurso compartido sin saber que es modificado al mismo tiempo por instrucciones desconocidas de los otros hilos. Esto da un resultado indeterminado . (Esto es realmente importante)

Un proceso de subproceso único no es más que una secuencia de instrucciones conocidas que, por lo tanto, da como resultado un resultado determinado , incluso si el orden de ejecución de las instrucciones no es fácil de leer en el código.

II) Pero no estamos seguros

II.1) Situaciones similares a las condiciones de carrera

Muchos lenguajes de programación implementan características de programación asíncrona a través de eventos o señales , manejadas por un bucle principal o un bucle de evento que verifica la cola de eventos y dispara a los oyentes. Ejemplo de esto son Javascript, libuevent, reactPHP, GNOME GLib ... A veces, podemos encontrar situaciones que parecen ser condiciones de carrera, pero no lo son .

La forma en que se llama al bucle de evento siempre se conoce , por lo que se determina el resultado, incluso si el orden de ejecución de las instrucciones no es fácil de leer (o incluso no se puede leer si no conocemos la biblioteca).

Ejemplo:

setTimeout( function() { console.log("EVENT LOOP CALLED"); }, 1 ); // We want to print EVENT LOOP CALLED after 1 milliseconds var now = new Date(); while(new Date() - now < 10) //We do something during 10 milliseconds console.log("EVENT LOOP NOT CALLED");

en Javascript la salida es siempre (puedes probar en node.js):

EVENT LOOP NOT CALLED EVENT LOOP CALLED

porque, el ciclo de eventos se llama cuando la pila está vacía (todas las funciones han regresado).

Tenga en cuenta que esto es solo un ejemplo y que en los lenguajes que implementan los eventos de una manera diferente, el resultado puede ser diferente, pero aún estaría determinado por la implementación.

II.2) Condición de carrera entre otros hilos, por ejemplo:

II.2.i) Con otros programas como clientes

Si otros procesos están solicitando nuestro proceso, que nuestro programa no trata las solicitudes de forma atómica y que nuestro proceso comparte algunos recursos entre las solicitudes, puede haber una condición de competencia entre los clientes .

Ejemplo:

var step; on(''requestOpen'')( function() { step = 0; } ); on(''requestData'')( function() { step = step + 1; } ); on(''requestEnd'')( function() { step = step +1; //step should be 2 after that sendResponse(step); } );

Aquí, tenemos una configuración de condición de carrera clásica. Si se abre una solicitud justo antes de que termine la otra, el step se restablecerá a 0. Si se requestData dos eventos requestEnd antes de requestEnd debido a dos solicitudes simultáneas, el paso llegará a 3. Pero esto se debe a que consideramos la secuencia de eventos como indeterminada. Esperamos que el resultado de un programa sea la mayoría del tiempo indeterminado con una entrada indeterminada.

De hecho, si nuestro programa es de un solo hilo, dada una secuencia de eventos, el resultado siempre está determinado. La condición de carrera es entre clientes .

Hay dos formas de entender el asunto:

  • Podemos considerar a los clientes como parte de nuestro programa (¿por qué no?) Y en este caso, nuestro programa es de múltiples hilos. El final de la historia.
  • Más comúnmente podemos considerar que los clientes no son parte de nuestro programa. En este caso, solo son entradas . Y cuando consideramos si un programa tiene un resultado determinado o no, lo hacemos con la entrada dada . De lo contrario, incluso la return input; programa más simple return input; tendría un resultado indeterminado.

Tenga en cuenta que :

  • si nuestro proceso trata la solicitud de forma atómica, es lo mismo que si hubiera un mutex entre el cliente y no hay una condición de carrera.
  • si podemos identificar la solicitud y adjuntar la variable a un objeto de solicitud que es el mismo en cada paso de la solicitud, no hay recurso compartido entre los clientes y ninguna condición de carrera

II.2.ii) Con hilo (s) de biblioteca

En nuestros programas, a menudo utilizamos bibliotecas que engendran otros procesos o hilos, o que solo hacen E / S con otros procesos (y las E / S siempre están indeterminadas).

Ejemplo:

databaseClient.sendRequest(''add Me to the database''); databaseClient.sendRequest(''remove Me from the database'');

Esto puede desencadenar una condición de carrera en una biblioteca asíncrona. Este es el caso si sendRequest() regresa después de haber enviado la solicitud a la base de datos, pero antes de que realmente se ejecute la solicitud. Inmediatamente enviamos otra solicitud y no podemos saber si la primera se ejecutará antes de que se evalúe la segunda porque la base de datos funciona en otra secuencia. Hay una condición de carrera entre el programa y el proceso de la base de datos .

Pero, si la base de datos estuviera en el mismo hilo que el programa (que en la vida real no sucede a menudo) sería imposible que sendRequest regrese antes de que se procese la solicitud. (A menos que la solicitud esté en cola, pero en este caso, el resultado aún se determina porque sabemos exactamente cómo y cuándo se lee la cola).

Conclusión

En resumen, los programas de un solo hilo no están exentos de las condiciones de carrera trigerring. Pero solo pueden ocurrir con o entre otros hilos de programas externos . El resultado de nuestro programa puede no estar determinado porque la información que nuestro programa recibe de esos otros programas no está determinada.