Skip to content

Latest commit

 

History

History
105 lines (89 loc) · 5.54 KB

README.md

File metadata and controls

105 lines (89 loc) · 5.54 KB

Ejemplo condiciones de carrera con hilos

Este repositorio contiene código ejemplo que permite la demostración de los efectos de condiciones de carrera en una aplicación cliente - servidor multihilo. En este ejemplo, el usuario ingresa una línea de texto desde el cliente, el cliente la envía al servidor, el servido calcula el hash sha256 de la cadena de texto y retorna este resultado en formato binario al cliente. El código de esta aplicación está basado en el capítulo 11 de Computer Systems: A Programmer's Perspective.

Uso

Para ejecutar el servidor se debe especificar como argumento el puerto TCP, por ejemplo:

./server 8080
server escuchando en puerto 8080...

Asumiendo que el servidor esta corriendo en una maquina con la IP 192.168.100 en el puerto 8080, ejemplo de ejecución del cliente:

./client 192.168.100 8080
Conectado exitosamente a 192.168.100 en el puerto 8080.
Ingrese texto para enviar al servidor, Ctrl+c para terminar...
> 

El servidor ignora el salto de línea al final de la cadena de caracteres enviada por el cliente. Ejemplo de funcionamiento correcto:

./client 192.168.100 8080
Conectado exitosamente a 192.168.100 en el puerto 8080.
Ingrese texto para enviar al servidor, Ctrl+c para terminar...
> hola
b221d9dbb083a7f33428d7c2a3c3198ae925614d70210e28716ccaa7cd4ddb79
> 

Si el servidor esta en la misma máquina, entonces es necesario abrir otra ventana/tab de terminal y ejecutar el cliente de esta forma:

$ ./client 127.0.0.1 8080
Conectado exitosamente a 127.0.0.1 en el puerto 8080.
Ingrese texto para enviar al servidor, Ctrl+c para terminar...
> 

El código en test.c envía la palabra "test" al servidor y comprueba si el servidor responde con el hash correcto. Muestra "OK" si es correcto e inmediatamente vuelve a enviar un nuevo "test". Caso contrario, muestra "BOOM" y se desconecta del servidor.

Demostración

Existen varias formas de provocar una condición de carrera en esta aplicación. La primera es eliminando el uso de memoria dinámica para almacenar el descriptor de archivo del socket de conexión en el servidor en el archivo server.c:

 68         pthread_t tid;                                                                                         
 69         while (seguir) {                                                                                       
 70                 connfdp = malloc(sizeof(int));                                                                 
 71                 *connfdp = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen);                       
 72                 pthread_create(&tid, NULL, thread, connfdp);                                                   
 73         }  

aquí se pueden modificar las líneas 70 - 72 a:

 68         pthread_t tid;                                                                                         
 69         while (seguir) {                                                                                       
 70                 int connfd;                                                                 
 71                 connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen);                       
 72                 pthread_create(&tid, NULL, thread, &connfd); //Condición de carrera                                                  
 73         }

Otra forma de condición de carrera es modificando la función void sha256_update en sha256.c para que no sea reentrante:

 99 void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)                                             
100 {                                                                                                              
101         WORD i;                                                                                                
102                                                                                                                
103         for (i = 0; i < len; ++i) {                                                                            
104                 ctx->data[ctx->datalen] = data[i];                                                             
105                 ctx->datalen++;  

la línea 101 puede modificarse así:

 99 void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)                                             
100 {                                                                                                              
101         static WORD i;       //Condición de carrera                                                                                           
102                                                                                                                
103         for (i = 0; i < len; ++i) {                                                                            
104                 ctx->data[ctx->datalen] = data[i];                                                             
105                 ctx->datalen++;  

Para observar el efecto de las condiciones de carrera es necesario conectar varios clientes test de manera simultánea al servidor.

Compilación

Para compilar cliente, servidor y test:

$ make

Para compilar solo el servidor:

$ make server

Para compilar cliente y servidor facilitando la depuración con gdb:

$ make debug

Para compilar cliente y servidor habilitando la herramienta AddressSanitizer, facilita la depuración en tiempo de ejecución:

$ make sanitize