Seguridad de memoria

La seguridad de memoria es la forma de estar protegido de varios errores de software y vulnerabilidades de seguridad cuando se realizan accesos a la memoria, como desbordamientos de buffer y punteros colgantes .[1]​ Por ejemplo, se dice que Java es seguro para la memoria porque su detección de errores en tiempo de ejecución verifica los límites de los arrays y los punteros desreferenciados. Por el contrario, C y C ++ permiten la aritmética de punteros, con punteros implementados como direcciones de memoria directas sin verificar los límites,[2]​ y por lo tanto son potencialmente inseguros para la memoria.[3]

Historia

Los errores de memoria se consideraron por primera vez en el contexto de la gestión de recursos y los sistemas de tiempo compartido, en un esfuerzo por evitar problemas como la Bomba fork.[4]​ Los desarrollos fueron principalmente teóricos hasta el gusano Morris, que explotó un desbordamiento de buffer en Finger .[5]​ El campo de la seguridad informática se desarrolló rápidamente a partir de entonces, intensificándose con la aparición de multitud de nuevos ataques como el ataque return-to-libc, las técnicas de defensa como la pila no ejecutable[6]​ y la aleatoriedad del diseño del espacio de direcciones . La aleatoriedad previene la mayoría de los ataques de desbordamiento de buffer y requiere que el atacante use heap spraying u otros métodos dependientes de la aplicación para obtener direcciones. Sin embargo, las implementaciones de la tecnología generalmente se limitan a la distribución aleatoria de bibliotecas y la ubicación de la pila.

Herramientas

DieHard,[7]​ su rediseño DieHarder[8]​ y la herramienta Allinea Distributed Debugging son asignadores de heap especiales que asignan objetos en su propia página de memoria virtual aleatoria, lo que permite detener y depurar lecturas y escrituras no válidas en la instrucción exacta que las provoca. La protección se basa en la protección de la memoria del hardware y, por lo tanto, la sobrecarga no suele ser sustancial, aunque puede aumentar significativamente si el programa hace un uso intensivo de la asignación.[9]​ La aleatoriedad proporciona solo protección probabilística contra errores de memoria.

La herramienta memcheck de Valgrind utiliza un simulador de conjunto de instrucciones y ejecuta el programa compilado en una máquina virtual de verificación de memoria, lo que garantiza la detección de un subconjunto de errores de memoria en tiempo de ejecución. Sin embargo, normalmente ralentiza el programa en un factor de 40,[10]​ y además debe ser informado explícitamente de los asignadores de memoria personalizados.[11][12]

Con acceso al código fuente, existen bibliotecas que recopilan y rastrean valores legítimos para punteros ("metadatos") y verifican cada acceso de puntero contra los metadatos para verificar su validez, como el recolector de basura Boehm .[13]​ En general, la seguridad de la memoria puede garantizarse de forma segura mediante el seguimiento de la recolección de basura y la inserción de comprobaciones de tiempo de ejecución en cada acceso a la memoria; este enfoque tiene gastos generales, pero menos que los de Valgrind. Todos los lenguajes de recolección de basura adoptan este enfoque.[1]

Para C y C ++, existen muchas herramientas que realizan una transformación del código en tiempo de compilación para realizar comprobaciones de seguridad de la memoria en tiempo de ejecución, como CheckPointer[14]​ y AddressSanitizer, que impone un factor de desaceleración promedio de 2.[15]

Otro enfoque utiliza el análisis de programa estático y la demostración automatizada de teoremas para garantizar que el programa no tenga errores de memoria. Por ejemplo, el lenguaje de programación Rust implementa un comprobador de préstamos para garantizar la seguridad de la memoria.[16]​ Herramientas como Coverity ofrecen análisis de memoria estática para C.[17]​ Los punteros inteligentes de C++ son una forma limitada de este enfoque.

Tipos de errores de memoria

Pueden ocurrir muchos tipos diferentes de errores de memoria:[18][19]

  • Errores de acceso : lectura o escritura inválidas de un puntero.
    • Desbordamiento del buffer : las escrituras fuera del buffer pueden dañar el contenido de los objetos adyacentes, los datos internos (como la información del heap ) o las direcciones de retorno.
    • Lecturas fuera del buffer: las lecturas fuera del buffer pueden revelar datos confidenciales o ayudar a los atacantes a evitar la distribución aleatoria del espacio de direcciones.
    • Condición de carrera : ocurre cuando se realizan lecturas o escrituras simultáneas en la memoria compartida.
    • Error de página no válida : se produce al acceder a un puntero fuera del espacio de memoria virtual. Una desreferencia de un puntero nulo a menudo causará una excepción o la terminación del programa en la mayoría de los entornos, pero puede causar daños en los núcleos del sistema operativo, en sistemas sin protección de memoria, o cuando el uso del puntero nulo implica un desplazamiento grande o negativo.
    • Usar después de liberar: sucede al desreferenciar un puntero colgante que almacena la dirección de un objeto que ha sido eliminado.
  • Variables no inicializadas - utilizar una variable a la que no se le ha asignado un valor puede contener un valor no deseado o corrupto.
    • <b>Desreferencia de un puntero nulo</b> : desreferenciar un puntero no válido o un puntero a la memoria que no se ha asignado
    • Punteros salvajes : surgen cuando se usa un puntero antes su inicialización. Muestran el mismo comportamiento errático que los punteros colgantes, aunque es menos probable que pasen desapercibidos.
  • Fuga de memoria - sucede cuando no se realiza un seguimiento del uso de la memoria o se realiza un seguimiento incorrecto
    • Desbordamiento de pila : ocurre cuando un programa se queda sin espacio en la pila, normalmente debido a una recursividad demasiado profunda. Una página de protección generalmente detiene el programa, evitando que se dañe la memoria, pero las funciones con marcos de pila grandes pueden omitir la página.
    • Desbordamieno del heap : se produce cuando un programa intenta asignar más memoria que la cantidad disponible.
    • Doble Liberación : ocurre cuando se realizan varias llamadas para liberar la memoria, esto puede provocar una pérdida de memoria si se llama con el mismo valor.
    • Liberación inválida : se produce al pasar una dirección no válida a la función la función malloc, esto puede dañar el heap.
    • Libre no coincidente : sucede cuando se utilizan varios asignadores y se intenta liberar la memoria con una función de liberación con un asignador diferente del que hizo la asignación.[20]
    • Alias no encontrado : ocurre cuando se asigna la misma ubicación de memoria y se modifica dos veces para fines no relacionados.

Referencias

  1. a b Dhurjati, Dinakar; Kowshik, Sumant; Adve, Vikram; Lattner, Chris (1 de enero de 2003). «Memory Safety Without Runtime Checks or Garbage Collection». Proceedings of the 2003 ACM SIGPLAN Conference on Language, Compiler, and Tool for Embedded Systems (en inglés) (ACM): 69-80. ISBN 1581136471. doi:10.1145/780732.780743. Consultado el 13 de marzo de 2017.  Error en la cita: Etiqueta <ref> no válida; el nombre «Adve» está definido varias veces con contenidos diferentes
  2. Koenig, Andrew. «How C Makes It Hard To Check Array Bounds». Dr. Dobb's. Consultado el 13 de marzo de 2017. 
  3. Akritidis, Periklis (June 2011). «Practical memory safety for C». Technical Report - University of Cambridge. Computer Laboratory (University of Cambridge, Computer Laboratory). ISSN 1476-2986. UCAM-CL-TR-798. Consultado el 13 de marzo de 2017. 
  4. Anderson, James P. Computer Security Planning Study 2. Electronic Systems Center. ESD-TR-73-51. 
  5. van der Veen, Victor; dutt-Sharma, Nitish; Cavallaro, Lorenzo; Bos, Herbert (2012). «Memory Errors: The Past, the Present, and the Future». Lecture Notes in Computer Science 7462 (RAID 2012): 86-106. ISBN 978-3-642-33337-8. doi:10.1007/978-3-642-33338-5_5. Consultado el 13 de marzo de 2017. 
  6. Wojtczuk, Rafal. «Defeating Solar Designer's Non-executable Stack Patch». insecure.org. Consultado el 13 de marzo de 2017. 
  7. Berger, Emery D.; Zorn, Benjamin G. (1 de enero de 2006). «DieHard: Probabilistic Memory Safety for Unsafe Languages». Proceedings of the 27th ACM SIGPLAN Conference on Programming Language Design and Implementation (en inglés) (ACM): 158-168. doi:10.1145/1133981.1134000. Consultado el 14 de marzo de 2017. 
  8. Novark, Gene; Berger, Emery D. (1 de enero de 2010). «DieHarder: Securing the Heap». Proceedings of the 17th ACM Conference on Computer and Communications Security (ACM): 573-584. doi:10.1145/1866307.1866371. Consultado el 14 de marzo de 2017. 
  9. «Memory Debugging in Allinea DDT». Archivado desde el original el 3 de febrero de 2015. 
  10. Gyllenhaal, John. «Using Valgrind's Memcheck Tool to Find Memory Errors and Leaks». computing.llnl.gov. Archivado desde el original el 7 de noviembre de 2018. Consultado el 13 de marzo de 2017. 
  11. «Memcheck: a memory error detector». Valgrind User Manual (en inglés). valgrind.org. Consultado el 13 de marzo de 2017. 
  12. Kreinin, Yossi. «Why custom allocators/pools are hard». Proper Fixation. Consultado el 13 de marzo de 2017. 
  13. «Using the Garbage Collector as Leak Detector». www.hboehm.info (en inglés estadounidense). Consultado el 14 de marzo de 2017. 
  14. «Semantic Designs: CheckPointer compared to other safety checking tools». www.semanticdesigns.com. Semantic Designs, Inc. 
  15. «AddressSanitizerPerformanceNumbers». 
  16. «References». The Rustonomicon (en inglés). Rust.org. Consultado el 13 de marzo de 2017. 
  17. Bessey, Al; Engler, Dawson; Block, Ken; Chelf, Ben; Chou, Andy; Fulton, Bryan; Hallem, Seth; Henri-Gros, Charles et al. (1 de febrero de 2010). «A few billion lines of code later». Communications of the ACM 53 (2): 66-75. doi:10.1145/1646353.1646374. Consultado el 14 de marzo de 2017. 
  18. Gv, Naveen. «How to Avoid, Find (and Fix) Memory Errors in your C/C++ Code». Cprogramming.com. Consultado el 13 de marzo de 2017. 
  19. «CWE-633: Weaknesses that Affect Memory». Community Weakness Enumeration (en inglés). MITRE. Consultado el 13 de marzo de 2017. 
  20. «CWE-762: Mismatched Memory Management Routines». Community Weakness Enumeration (en inglés). MITRE. Consultado el 13 de marzo de 2017.