2012-08-14 25 views
16

En un comentario sobre la cuestión Automatically release mutex on crashes in Unix en 2010, Jilles afirmó: mutex robustosCondición de carrera en glibc/NPTL/Linux mutexes robustos?

de glibc son tan rápido porque glibc toma atajos peligrosos. No hay garantía de que el mutex todavía exista cuando el kernel lo marque como "causará EOWNERDEAD". Si el mutex fue destruido y la memoria reemplazada por un archivo mapeado de memoria que contiene la última identificación del hilo propietario en el lugar correcto y el último hilo propietario termina justo después de escribir la palabra de bloqueo (pero antes de eliminar completamente el mutex de su lista de mutexes propios), el archivo está dañado. Los mutexes robustos de Solaris y Will-be-FreeBSD9 son más lentos porque no quieren tomar este riesgo.

No puedo entender el reclamo, ya que destruir un mutex no es legal a menos que esté desbloqueado (y por lo tanto no en la lista robusta de ningún hilo). Tampoco puedo encontrar ninguna referencia buscando tal error/problema. ¿El reclamo era simplemente erróneo?

La razón por la que pregunto y estoy interesado es porque esto es relevante para la corrección de mi propia implementación basada en la misma primitiva robusta-mutex de Linux.

+3

omg, el nombre del error tiene NERD en él –

+0

Parece que el viejo enfoque basado en VMA tenía algunos problemas al menos; http://www.kernel.org/doc/Documentation/robust-futexes.txt. Sin embargo, si lo leo correctamente, la lista se mantiene en la memoria de espacio de usuario, entonces, ¿qué debe hacer si esa memoria está dañada? Aunque tal vez solo pueda ser visto como un caso especial de corrupción de la memoria compartida. – nos

+0

Sí, veo que la lista o incluso los contenidos mutex podrían corromperse si el proceso se ejecuta de forma frenética y los corta. ¿Este es el problema que se describe? No me preocupa garantizar un comportamiento adecuado cuando un proceso con acceso al mutex ha invocado un comportamiento indefinido; Solo estoy preocupado por la posibilidad de alguna condición de carrera en el uso bien definido del mutex robusto. –

Respuesta

6

La descripción de la raza por FreeBSD pthread desarrollador David Xu: http://lists.freebsd.org/pipermail/svn-src-user/2010-November/003668.html

No creo que el ciclo munmap/mmap es estrictamente necesario para la carrera. La pieza de memoria compartida también puede usarse de forma diferente. Esto es poco común pero válido.

Como también se menciona en ese mensaje, más "diversión" ocurre si los subprocesos con privilegios diferentes acceden a un mutex robusto común. Debido a que el nodo para la lista de mutexes robustos propios está en el mutex mismo, un hilo con privilegios bajos puede dañar la lista de un hilo de alto privilegio.Esto podría aprovecharse fácilmente para hacer que el alto subproceso de privilegios se bloquee y, en casos excepcionales, esto podría permitir la corrupción de la memoria del subproceso de privilegios. Aparentemente, los muteos robustos de Linux solo están diseñados para ser utilizados por hilos con los mismos privilegios. Esto podría haberse evitado fácilmente haciendo que la lista robusta sea una matriz completamente en la memoria del subproceso en lugar de una lista vinculada.

+1

Gracias por captar el problema de poner la memoria a un uso diferente sin desasignarla. Esto debería evitarse poniendo algún tipo de sincronización en 'pthread_mutex_destroy' ... –

+0

En realidad, me pregunto si es posible evitar ese problema. Dentro del mismo proceso, definitivamente se puede evitar, pero entre procesos múltiples, no hay una forma obvia de "esperar a que un proceso tenga un puntero obsoleto pendiente al mutex para soltarlo". No creo que este problema sea tan grave como el problema del "nuevo mapeo" (ya que solo afecta a un caso de uso muy especial, reutilizando la misma memoria compartida para un propósito diferente), pero sigue siendo bastante preocupante. –

+1

En cuanto a su oración final, la solución "fácil" que utiliza una matriz en lugar de una lista vinculada no es realmente fácil. No tienes almacenamiento para una matriz. El requisito de almacenamiento crece linealmente con el número de mutexes robustos que contiene el hilo, y la única forma de obtener ese almacenamiento sin asignación adicional (que podría fallar) en el momento del bloqueo es colocarlo en los mutex. Estoy de acuerdo en que es desafortunado, pero parece que no hay otra manera. La mayoría de las soluciones de kernel-space también requieren asignación en el momento del bloqueo, que puede fallar, haciendo que el bloqueo sea inútil ... –

8

Creo que encontré la carrera, y de hecho es muy fea. Funciona de la siguiente manera:

El subproceso A ha mantenido el mutex robusto y lo desbloquea. El procedimiento básico es:

  1. Colóquelo en la ranura "pendiente" del encabezado de la lista robusta de la secuencia.
  2. Elimínelo de la lista vinculada de muteos robustos mantenidos por el hilo actual.
  3. Desbloquee el mutex.
  4. Borre la ranura "pendiente" del encabezado de la lista robusta de la secuencia.

El problema es que entre los pasos 3 y 4, otro hilo en el mismo proceso podría obtener el mutex, luego desbloquearlo, y (correctamente) creyendo ser el usuario final del mutex, destruir y liberar/munmap it. Después de eso, si un hilo en el proceso crea una asignación compartida de un archivo, dispositivo o memoria compartida, se le asigna la misma dirección, y el valor en esa ubicación coincide con el pid del hilo que todavía está entre los pasos 3 y 4 de desbloqueo, usted tiene una situación en la que, si el proceso se cancela, el kernel corromperá el archivo asignado estableciendo el bit alto de un entero de 32 bits que cree que es el ID del propietario del mutex.

La solución es llevar a cabo un bloqueo global sobre mmap/munmap entre los pasos 2 y 4 anteriores, exactamente igual que en mi solución al problema de barrera descrito en mi respuesta a esta pregunta:

Can a correct fail-safe process-shared barrier be implemented on Linux?

+0

@r Entiendo bien que solo puede evitar este problema cuando está escribiendo su propia implementación de mutex y tiene control sobre las llamadas 'mmap' /' munmap' (para que pueda insertar las cerraduras entre los pasos 2 y 4)? Eso significa que los extensores robustos POSIX (pthreads) siempre sufrirán este problema. ¿Debería considerarse una falla pthreads en Linux? Además, ¿qué haces cuando falla el titular del bloqueo de mmap global (robusto o no?). – nh2

+0

Sí, es un error en glibc. Consulte https://sourceware.org/bugzilla/show_bug.cgi?id=14485 –

Cuestiones relacionadas