De Wikipedia, la enciclopedia libre
  (Redirigido desde la infracción de segmentación )
Saltar a navegación Saltar a búsqueda

En computación , un fallo de segmentación (a menudo abreviado como violación de segmento ) o violación de acceso es un fallo o condición de fallo, planteada por el hardware con protección de memoria , notificando un sistema operativo (OS) en el software ha intentado acceder a una zona restringida de la memoria (una violación de acceso a la memoria). En equipos x86 estándar , esta es una forma de falla de protección general . El kernel del sistema operativo , en respuesta, generalmente realizará alguna acción correctiva, generalmente transmitiendo la falla al proceso infractor mediante el envío de una señal al proceso.. En algunos casos, los procesos pueden instalar un manejador de señales personalizado, lo que les permite recuperarse por sí mismos, [1] pero de lo contrario se usa el manejador de señales predeterminado del sistema operativo, lo que generalmente causa una terminación anormal del proceso (un bloqueo del programa ) y, a veces, un volcado del núcleo. .

Las fallas de segmentación son una clase común de error en programas escritos en lenguajes como C que brindan acceso a memoria de bajo nivel. Surgen principalmente debido a errores en el uso de punteros para el direccionamiento de la memoria virtual , particularmente el acceso ilegal. Otro tipo de error de acceso a la memoria es un error de bus , que también tiene varias causas, pero hoy en día es mucho más raro; estos ocurren principalmente debido a un direccionamiento incorrecto de la memoria física , o debido a un acceso a la memoria desalineado; se trata de referencias de memoria que el hardware no puede abordar, en lugar de referencias que un proceso no puede abordar.

Muchos lenguajes de programación pueden emplear mecanismos diseñados para evitar fallas de segmentación y mejorar la seguridad de la memoria. Por ejemplo, el lenguaje de programación Rust emplea un modelo basado en 'Propiedad' [2] para garantizar la seguridad de la memoria. [3] Otros lenguajes, como Lisp y Java , emplean la recolección de basura, [4] que evita ciertas clases de errores de memoria que podrían conducir a fallas de segmentación. [5]

Resumen [ editar ]

Ejemplo de señal generada por humanos
Una desreferencia de puntero nulo en Windows 8

Una falla de segmentación ocurre cuando un programa intenta acceder a una ubicación de memoria a la que no se le permite acceder, o intenta acceder a una ubicación de memoria de una manera que no está permitida (por ejemplo, al intentar escribir en una ubicación de solo lectura , o para sobrescribir parte del sistema operativo ).

El término "segmentación" tiene varios usos en informática; en el contexto de "falla de segmentación", un término utilizado desde la década de 1950, [ cita requerida ] se refiere al espacio de direcciones de un programa. [6] Con protección de memoria, solo se puede leer el propio espacio de direcciones del programa, y ​​de esto, solo la pila y la parte de lectura / escritura del segmento de datos de un programa se pueden escribir, mientras que los datos de solo lectura y el segmento de código no se pueden escribir. escribible. Por lo tanto, intentar leer fuera del espacio de direcciones del programa o escribir en un segmento de solo lectura del espacio de direcciones da como resultado un error de segmentación, de ahí el nombre.

En los sistemas que utilizan la segmentación de la memoria de hardware para proporcionar memoria virtual , se produce una falla de segmentación cuando el hardware detecta un intento de hacer referencia a un segmento inexistente, o de hacer referencia a una ubicación fuera de los límites de un segmento, o de hacer referencia a una ubicación en una moda no permitida por los permisos otorgados para ese segmento. En los sistemas que solo usan paginación , una falla de página no válida generalmente conduce a una falla de segmentación, y las fallas de segmentación y las fallas de página son ambas fallas generadas por la memoria virtualsistema de gestión. Las fallas de segmentación también pueden ocurrir independientemente de las fallas de página: el acceso ilegal a una página válida es una falla de segmentación, pero no una falla de página no válida, y las fallas de segmentación pueden ocurrir en el medio de una página (por lo tanto, no hay fallas de página), por ejemplo en un desbordamiento de búfer que permanece dentro de una página pero sobrescribe ilegalmente la memoria.

A nivel de hardware, la unidad de gestión de memoria (MMU) plantea inicialmente la falla en caso de acceso ilegal (si la memoria referenciada existe), como parte de su función de protección de memoria, o una falla de página no válida (si la memoria referenciada no existe) ). Si el problema no es una dirección lógica no válida, sino una dirección física no válida, se genera un error de bus , aunque no siempre se distinguen.

A nivel del sistema operativo, se detecta esta falla y se transmite una señal al proceso infractor, lo que activa el controlador del proceso para esa señal. Los diferentes sistemas operativos tienen diferentes nombres de señales para indicar que se ha producido una falla de segmentación. En sistemas operativos similares a Unix, se envía una señal llamada SIGSEGV (abreviado de violación de segmentación ) al proceso infractor. En Microsoft Windows , el proceso infractor recibe una excepción STATUS_ACCESS_VIOLATION .

Causas [ editar ]

Las condiciones bajo las cuales ocurren las violaciones de segmentación y cómo se manifiestan son específicas del hardware y del sistema operativo: un hardware diferente genera diferentes fallas para condiciones dadas, y diferentes sistemas operativos las convierten en diferentes señales que se transmiten a los procesos. La causa próxima es una infracción de acceso a la memoria, mientras que la causa subyacente es generalmente un error de software de algún tipo. Determinar la causa raíz , depurar el error, puede ser simple en algunos casos, donde el programa provocará constantemente una falla de segmentación (por ejemplo, desreferenciar un puntero nulo ), mientras que en otros casos el error puede ser difícil de reproducir y dependerá de la asignación de memoria. en cada ejecución (por ejemplo, desreferenciar unpuntero colgante ).

Las siguientes son algunas de las causas típicas de una falla de segmentación:

  • Intentar acceder a una dirección de memoria inexistente (fuera del espacio de direcciones del proceso)
  • Intentar acceder a la memoria a la que el programa no tiene derechos (como las estructuras del kernel en el contexto del proceso)
  • Intentar escribir memoria de solo lectura (como un segmento de código)

Estos, a su vez, a menudo se deben a errores de programación que provocan un acceso no válido a la memoria:

  • Desreferenciar un puntero nulo , que generalmente apunta a una dirección que no forma parte del espacio de direcciones del proceso
  • Desreferenciar o asignar a un puntero no inicializado (puntero salvaje , que apunta a una dirección de memoria aleatoria)
  • Desreferenciar o asignar a un puntero liberado (puntero colgante , que apunta a la memoria que se ha liberado / desasignado / eliminado)
  • Un desbordamiento de búfer
  • Un desbordamiento de pila
  • Intentar ejecutar un programa que no se compila correctamente. (Algunos compiladores generarán un archivo ejecutable a pesar de la presencia de errores en tiempo de compilación).

En el código C, violaciones de segmento con mayor frecuencia se producen debido a errores en el uso del puntero, en particular en la asignación de memoria dinámica C . Desreferenciar un puntero nulo siempre resultará en un error de segmentación, pero los punteros salvajes y los punteros colgantes apuntan a la memoria que puede o no existir, y puede o no ser legible o escribible, y por lo tanto puede resultar en errores transitorios. Por ejemplo:

char  * p1  =  NULL ;  // Puntero nulo char  * p2 ;  // Puntero salvaje: no inicializado en absoluto. char  * p3  =  malloc ( 10  *  tamaño de ( char ));  // Puntero inicializado a la memoria asignada  // (asumiendo que malloc no falló) free ( p3 );  // p3 ahora es un puntero colgante, ya que se ha liberado la memoria

Ahora, desreferenciar cualquiera de estas variables podría causar una falla de segmentación: desreferenciar el puntero nulo generalmente causará una falta de segmentación, mientras que la lectura del puntero salvaje puede resultar en datos aleatorios pero no de segmentación, y la lectura del puntero colgante puede resultar en datos válidos durante un tiempo, y luego los datos aleatorios a medida que se sobrescriben.

Manejo [ editar ]

La acción predeterminada para una falla de segmentación o un error de bus es la terminación anormal del proceso que la desencadenó. Se puede generar un archivo central para ayudar a la depuración y también se pueden realizar otras acciones dependientes de la plataforma. Por ejemplo, los sistemas Linux que usan el parche grsecurity pueden registrar señales SIGSEGV para monitorear posibles intentos de intrusión usando desbordamientos de búfer .

En algunos sistemas, como Linux y Windows, es posible que el propio programa maneje una falla de segmentación. [7] Dependiendo de la arquitectura y el sistema operativo, el programa en ejecución no solo puede manejar el evento, sino que puede extraer información sobre su estado, como obtener un seguimiento de la pila, los valores de registro del procesador, la línea del código fuente cuando se activó, la memoria. dirección a la que se accedió de forma inválida [8] y si la acción fue de lectura o escritura. [9]

Aunque una falla de segmentación generalmente significa que el programa tiene un error que necesita ser arreglado, también es posible causar intencionalmente tal falla con el propósito de probar, depurar y también para emular plataformas donde se necesita acceso directo a la memoria. En el último caso, el sistema debe poder permitir que el programa se ejecute incluso después de que ocurra la falla. En este caso, cuando el sistema lo permite, es posible manejar el evento e incrementar el contador del programa del procesador para "saltar" sobre la instrucción fallida para continuar la ejecución. [10]

Ejemplos [ editar ]

Fallo de segmentación en un teclado EMV

Escribiendo en la memoria de solo lectura [ editar ]

Escribir en la memoria de solo lectura genera un error de segmentación. En el nivel de errores de código, esto ocurre cuando el programa escribe en parte de su propio segmento de código o en la parte de solo lectura del segmento de datos , ya que el sistema operativo los carga en la memoria de solo lectura.

Aquí hay un ejemplo de código ANSI C que generalmente causará un error de segmentación en plataformas con protección de memoria. Intenta modificar un literal de cadena , que es un comportamiento indefinido de acuerdo con el estándar ANSI C. La mayoría de los compiladores no detectarán esto en el momento de la compilación y, en su lugar, lo compilarán en un código ejecutable que fallará:

int  main ( void ) {  char  * s  =  "hola mundo" ;  * s  =  'H' ; }

Cuando se compila el programa que contiene este código, la cadena "hola mundo" se coloca en la sección rodata del archivo ejecutable del programa : la sección de solo lectura del segmento de datos . Cuando se carga, el sistema operativo lo coloca con otras cadenas y datos constantes en un segmento de memoria de solo lectura. Cuando se ejecuta, se establece una variable, s , para que apunte a la ubicación de la cadena y se intenta escribir un carácter H a través de la variable en la memoria, lo que provoca una falla de segmentación. Compilar un programa de este tipo con un compilador que no comprueba la asignación de ubicaciones de solo lectura en tiempo de compilación y ejecutarlo en un sistema operativo similar a Unix produce lo siguienteerror de tiempo de ejecución :

$ gcc segfault.c -g -o segfault $ ./segfault Fallo de segmentación

Seguimiento del archivo principal de GDB :

Programa  recibido  señal  SIGSEGV ,  Fallo de segmentación  . 0x1c0005c2 en main () en segfault . c : 6 6 * s = 'H' ;        

Este código se puede corregir usando una matriz en lugar de un puntero de carácter, ya que esto asigna memoria en la pila y la inicializa con el valor del literal de cadena:

char  s []  =  "hola mundo" ; s [ 0 ]  =  'H' ;  // de forma equivalente, * s = 'H';

Aunque los literales de cadena no deben modificarse (esto tiene un comportamiento indefinido en el estándar C), en C son de static char []tipo, [11] [12] [13] por lo que no hay conversión implícita en el código original (que apunta a char *en esa matriz), mientras que en C ++ son de static const char []tipo y, por lo tanto, hay una conversión implícita, por lo que los compiladores generalmente detectarán este error en particular.

Desreferencia de puntero nulo [ editar ]

En lenguajes C y similares a C, los punteros nulos se utilizan para significar "puntero a ningún objeto" y como indicador de error, y desreferenciar un puntero nulo (una lectura o escritura a través de un puntero nulo) es un error de programa muy común. El estándar C no dice que el puntero nulo sea el mismo que el puntero a la dirección de memoria  0, aunque ese puede ser el caso en la práctica. La mayoría de los sistemas operativos mapean la dirección del puntero nulo de tal manera que acceder a él provoca una falla de segmentación. Este comportamiento no está garantizado por el estándar C. Desreferenciar un puntero nulo es un comportamiento indefinido en C, y una implementación conforme puede asumir que cualquier puntero desreferenciado no es nulo.

int  * ptr  =  NULL ; printf ( "% d" ,  * ptr );

Este código de muestra crea un puntero nulo y luego intenta acceder a su valor (leer el valor). Hacerlo provoca un error de segmentación en tiempo de ejecución en muchos sistemas operativos.

Desreferenciar un puntero nulo y luego asignarle (escribir un valor en un objetivo inexistente) también suele causar una falla de segmentación:

int  * ptr  =  NULL ; * ptr  =  1 ;

El siguiente código incluye una desreferenciación de puntero nulo, pero cuando se compila a menudo no dará como resultado una falla de segmentación, ya que el valor no se utiliza y, por lo tanto, la desreferencia a menudo se optimizará mediante la eliminación del código muerto :

int  * ptr  =  NULL ; * ptr ;

Desbordamiento de búfer [ editar ]

Desbordamiento de pila [ editar ]

Otro ejemplo es la recursividad sin un caso base:

int  main ( vacío ) {  main ();  return  0 ; }

lo que hace que la pila se desborde, lo que resulta en una falla de segmentación. [14] La recursividad infinita puede no resultar necesariamente en un desbordamiento de la pila dependiendo del lenguaje, las optimizaciones realizadas por el compilador y la estructura exacta de un código. En este caso, el comportamiento del código inalcanzable (la declaración de retorno) no está definido, por lo que el compilador puede eliminarlo y usar una optimización de llamada de cola que podría resultar en un uso de la pila sin uso. Otras optimizaciones podrían incluir traducir la recursividad en iteración, lo que dada la estructura de la función de ejemplo daría como resultado que el programa se ejecute para siempre, aunque probablemente no se desborde de su pila.

Ver también [ editar ]

  • Volcado de memoria
  • Error de protección general
  • Fallo de página
  • Violación de almacenamiento
  • Meditación Gurú

Referencias [ editar ]

  1. ^ Programación experta en C: secretos profundos de C Por Peter Van der Linden, página 188
  2. ^ El lenguaje de programación Rust - Propiedad
  3. ^ Concurrencia intrépida con Rust: el blog del lenguaje de programación Rust
  4. ^ McCarthy, John (abril de 1960). "Funciones recursivas de expresiones simbólicas y su computación por máquina, Parte I" . Comunicaciones de la ACM . 4 (3): 184-195. doi : 10.1145 / 367177.367199 . S2CID  1489409 . Consultado el 22 de septiembre de 2018 .
  5. ^ Dhurjati, Dinakar; Kowshik, Sumant; Adve, Vikram; Lattner, Chris (1 de enero de 2003). "Seguridad de la memoria sin comprobaciones en tiempo de ejecución o recolección de basura" (PDF) . Actas de la Conferencia ACM SIGPLAN de 2003 sobre lenguaje, compilador y herramienta para sistemas integrados . ACM: 69–80. doi : 10.1145 / 780732.780743 . ISBN  1581136471. S2CID  1459540 . Consultado el 22 de septiembre de 2018 .
  6. ^ "Depuración de errores de segmentación y problemas de puntero - Cprogramming.com" . www.cprogramming.com . Consultado el 3 de febrero de 2021 .
  7. ^ "Recuperación limpia de Segfaults en Windows y Linux (32 bits, x86)" . Consultado el 23 de agosto de 2020 .
  8. ^ "Implementación del controlador SIGSEGV / SIGABRT que imprime el seguimiento de la pila de depuración" . Consultado el 23 de agosto de 2020 .
  9. ^ "¿Cómo identificar operaciones de lectura o escritura de fallas de página cuando se usa el manejador sigaction en SIGSEGV? (LINUX)" . Consultado el 23 de agosto de 2020 .
  10. ^ "LINUX - MANIPULADORES DE FALTAS DE ESCRITURA" . Consultado el 23 de agosto de 2020 .
  11. ^ "6.1.4 Literales de cadena". ISO / IEC 9899: 1990 - Lenguajes de Programación - C .
  12. ^ "6.4.5 Literales de cadena". ISO / IEC 9899: 1999 - Lenguajes de Programación - C .
  13. ^ "6.4.5 Literales de cadena". ISO / IEC 9899: 2011 - Lenguajes de Programación - C .
  14. ^ ¿Cuál es la diferencia entre un error de segmentación y un desbordamiento de pila? en Stack Overflow

Enlaces externos [ editar ]

  • Proceso: límite de enfoque y falla de segmentación
  • Preguntas frecuentes: respuestas aportadas por el usuario sobre la definición de un error de segmentación
  • Explicación de un "puntero nulo"
  • Responda a: ¿Se garantiza que NULL sea 0, pero el puntero nulo no?
  • Las especificaciones de la base de grupo abierto Issue 6 signal.h