La protección de memoria es una forma de controlar los derechos de acceso a la memoria en una computadora y es parte de la mayoría de las arquitecturas de conjuntos de instrucciones y sistemas operativos modernos . El objetivo principal de la protección de memoria es evitar que un proceso acceda a la memoria que no se le ha asignado. Esto evita que un error o malware dentro de un proceso afecte a otros procesos o al sistema operativo en sí. La protección puede abarcar todos los accesos a un área específica de memoria, accesos de escritura o intentos de ejecutar el contenido del área. Un intento de acceder a una memoria no autorizada da como resultado un error de hardware , por ejemplo, un error de segmentación , una excepción de violación de almacenamiento , que generalmente causa la terminación anormal del proceso infractor. La protección de memoria para la seguridad informática incluye técnicas adicionales como la aleatorización del diseño del espacio de direcciones y la protección del espacio ejecutable .
La segmentación se refiere a la división de la memoria de una computadora en segmentos. Una referencia a una ubicación de memoria incluye un valor que identifica un segmento y un desplazamiento dentro de ese segmento. Un descriptor de segmento puede limitar los derechos de acceso, por ejemplo, solo lectura, solo desde ciertos anillos .
La arquitectura x86 tiene múltiples funciones de segmentación, que son útiles para usar memoria protegida en esta arquitectura. [1] En la arquitectura x86, la Tabla de descriptores globales y las Tablas de descriptores locales se pueden usar para hacer referencia a segmentos en la memoria de la computadora. Los punteros a segmentos de memoria en procesadores x86 también se pueden almacenar en los registros de segmento del procesador. Inicialmente, los procesadores x86 tenían 4 registros de segmento, CS (segmento de código), SS (segmento de pila), DS (segmento de datos) y ES (segmento adicional); más tarde se agregaron otros dos registros de segmento: FS y GS. [1]
En la paginación, el espacio o segmento de direcciones de memoria se divide en bloques de igual tamaño [b] llamados páginas . Si se utiliza hardware de memoria virtual , cada página puede residir en cualquier ubicación en un límite adecuado de la memoria física de la computadora, o puede marcarse como protegida. La memoria virtual permite tener un espacio de direcciones de memoria virtual lineal y usarlo para acceder a bloques fragmentados en el espacio de direcciones de memoria física .
La mayoría de las arquitecturas de computadoras que admiten la paginación también utilizan páginas como base para la protección de la memoria.
Una tabla de páginas asigna la memoria virtual a la memoria física. Puede haber una sola tabla de páginas, una tabla de páginas para cada proceso, una tabla de páginas para cada segmento o una jerarquía de tablas de páginas, según la arquitectura y el sistema operativo. Las tablas de páginas suelen ser invisibles para el proceso. Las tablas de páginas facilitan la asignación de memoria adicional, ya que cada página nueva se puede asignar desde cualquier lugar de la memoria física. En algunos sistemas, una entrada de la tabla de páginas también puede designar una página como de solo lectura.
Algunos sistemas operativos configuran un espacio de direcciones diferente para cada proceso, lo que proporciona límites de protección de memoria estrictos. [2] Es imposible que una aplicación sin privilegios [c] acceda a una página que no se le haya asignado explícitamente, porque cada dirección de memoria apunta a una página asignada a esa aplicación o genera una interrupción llamada error de página . Las páginas no asignadas y las páginas asignadas a cualquier otra aplicación no tienen ninguna dirección desde el punto de vista de la aplicación.
Un fallo de página no necesariamente indica un error. Los fallos de página no solo se utilizan para proteger la memoria. El sistema operativo puede gestionar la tabla de páginas de tal forma que una referencia a una página que se ha paginado previamente al almacenamiento secundario [d] provoque un fallo de página. El sistema operativo intercepta el fallo de página, carga la página de memoria requerida y la aplicación continúa como si no se hubiera producido ningún fallo. Este esquema, un tipo de memoria virtual , permite que los datos en memoria que no se utilizan actualmente se muevan al almacenamiento secundario y vuelvan de una forma que sea transparente para las aplicaciones, para aumentar la capacidad de memoria general.
En algunos sistemas, una solicitud de almacenamiento virtual puede asignar un bloque de direcciones virtuales para las que no se han asignado marcos de página, y el sistema solo asignará e inicializará marcos de página cuando se produzcan errores de página. En algunos sistemas, se puede utilizar una página de protección , ya sea para la detección de errores o para hacer crecer automáticamente las estructuras de datos.
En algunos sistemas, el mecanismo de error de página también se utiliza para la protección del espacio ejecutable, como W^X .
Un mecanismo de clave de protección de memoria (MPK) [3] divide la memoria física en bloques de un tamaño particular (por ejemplo, 4 KiB), cada uno de los cuales tiene un valor numérico asociado llamado clave de protección. Cada proceso también tiene un valor de clave de protección asociado. En un acceso a la memoria, el hardware verifica que la clave de protección del proceso actual coincida con el valor asociado con el bloque de memoria al que se accede; si no, se produce una excepción. Este mecanismo se introdujo en la arquitectura System/360 . Está disponible en los mainframes System z actuales y es muy utilizado por los sistemas operativos System z y sus subsistemas.
Las claves de protección System/360 descritas anteriormente están asociadas a direcciones físicas. Esto es diferente del mecanismo de clave de protección utilizado por arquitecturas como Hewlett-Packard / Intel IA-64 y Hewlett-Packard PA-RISC , que están asociadas a direcciones virtuales y que permiten múltiples claves por proceso.
En las arquitecturas Itanium y PA-RISC, las traducciones ( entradas TLB ) tienen claves (Itanium) o identificadores de acceso (PA-RISC) asociados a ellas. Un proceso en ejecución tiene varios registros de clave de protección (16 para Itanium, [4] 4 para PA-RISC [5] ). Una traducción seleccionada por la dirección virtual tiene su clave comparada con cada uno de los registros de clave de protección. Si alguno de ellos coincide (más otras comprobaciones posibles), se permite el acceso. Si ninguno coincide, se genera un fallo o una excepción. El gestor de fallos de software puede, si lo desea, comprobar la clave que falta con una lista más grande de claves mantenidas por software; por tanto, los registros de clave de protección dentro del procesador pueden tratarse como una caché gestionada por software de una lista más grande de claves asociadas a un proceso.
PA-RISC tiene entre 15 y 18 bits de clave; Itanium exige al menos 18. Las claves suelen estar asociadas a dominios de protección , como bibliotecas, módulos, etc.
En x86, la arquitectura de claves de protección [6] permite etiquetar direcciones virtuales para páginas de usuario con cualquiera de las 16 claves de protección. Todas las páginas etiquetadas con la misma clave de protección constituyen un dominio de protección. Un nuevo registro contiene los permisos asociados con cada uno de los dominios de protección. Las operaciones de carga y almacenamiento se verifican con los permisos de la tabla de páginas y los permisos de la clave de protección asociados con el dominio de protección de la dirección virtual, y solo se permiten si ambos permisos permiten el acceso. Los permisos de la clave de protección se pueden configurar desde el espacio de usuario, lo que permite que las aplicaciones restrinjan directamente el acceso a los datos de la aplicación sin la intervención del sistema operativo. Dado que las claves de protección están asociadas con una dirección virtual, los dominios de protección son por espacio de direcciones, por lo que los procesos que se ejecutan en diferentes espacios de direcciones pueden usar los 16 dominios.
En Multics y sistemas derivados de él, cada segmento tiene un anillo de protección para lectura, escritura y ejecución; un intento por parte de un proceso con un número de anillo mayor que el número de anillo del segmento provoca un fallo. Existe un mecanismo para llamar de forma segura a procedimientos que se ejecutan en un anillo inferior y regresar al anillo superior. Existen mecanismos para que una rutina que se ejecuta con un número de anillo bajo acceda a un parámetro con el mayor de los dos: su propio anillo y el anillo del que realiza la llamada.
La simulación es el uso de un programa de monitoreo para interpretar las instrucciones de código de máquina de algunas arquitecturas de computadoras. Un simulador de conjunto de instrucciones de este tipo puede proporcionar protección de memoria mediante el uso de un esquema similar a la segmentación y la validación de la dirección de destino y la longitud de cada instrucción en tiempo real antes de ejecutarlas. El simulador debe calcular la dirección de destino y la longitud y compararlas con una lista de rangos de direcciones válidos que posee en relación con el entorno del hilo , como cualquier bloque de memoria dinámica adquirido desde el inicio del hilo, más cualquier ranura de memoria estática compartida válida. El significado de "válido" puede cambiar a lo largo de la vida del hilo dependiendo del contexto. A veces se puede permitir alterar un bloque estático de almacenamiento, y a veces no, dependiendo del modo actual de ejecución, que puede o no depender de una clave de almacenamiento o un estado de supervisor. [ cita requerida ]
En general, no es recomendable utilizar este método de protección de memoria cuando existen instalaciones adecuadas en una CPU, ya que esto le quita una valiosa capacidad de procesamiento a la computadora. Sin embargo, generalmente se utiliza con fines de depuración y prueba para brindar un nivel de granularidad extra fino a violaciones de almacenamiento genéricas y puede indicar con precisión qué instrucción está intentando sobrescribir la sección particular de almacenamiento que puede tener la misma clave de almacenamiento que el almacenamiento sin protección.
El direccionamiento basado en capacidades es un método de protección de memoria que no se utiliza en las computadoras comerciales modernas. En este método, los punteros se reemplazan por objetos protegidos (llamados capacidades ) que solo se pueden crear utilizando instrucciones privilegiadas que solo pueden ser ejecutadas por el núcleo o algún otro proceso autorizado para hacerlo. [ cita requerida ] Esto permite efectivamente que el núcleo controle qué procesos pueden acceder a qué objetos en la memoria, sin necesidad de usar espacios de direcciones separados o cambios de contexto . Solo unos pocos productos comerciales usaban seguridad basada en capacidades: Plessey System 250 , IBM System/38 , arquitectura Intel iAPX 432 y KeyKOS . Los enfoques de capacidad se usan ampliamente en sistemas de investigación como EROS y el navegador Combex DARPA. Se usan conceptualmente como base para algunas máquinas virtuales , más notablemente Smalltalk y Java . Actualmente, el proyecto CHERI financiado por DARPA en la Universidad de Cambridge está trabajando para crear una máquina de capacidad moderna que también admita software heredado.
La contaminación dinámica es una técnica para proteger los programas de accesos ilegales a la memoria. Cuando se asigna memoria, en tiempo de ejecución, esta técnica contamina tanto la memoria como el puntero correspondiente utilizando la misma marca de contaminación. Las marcas de contaminación se propagan adecuadamente mientras se ejecuta el programa y se verifican cada vez que se accede a una dirección de memoria m a través de un puntero p ; si las marcas de contaminación asociadas con m y p difieren, se detiene la ejecución y se informa del acceso ilegal. [7] [8]
Los procesadores SPARC M7 (y superiores) implementan la función de contaminación dinámica en el hardware. Oracle comercializa esta función como memoria protegida de silicio (SSM) (anteriormente denominada Application Data Integrity (ADI)). [9]
El diseño de la CPU lowRISC incluye contaminación dinámica bajo el nombre de memoria etiquetada. [10]
El nivel de protección de una implementación particular puede medirse por su adherencia al principio de privilegio mínimo . [11]
Los distintos sistemas operativos utilizan distintas formas de protección o separación de la memoria. Aunque la protección de la memoria era común en la mayoría de los mainframes y en muchos sistemas de minicomputadoras desde la década de 1960, la verdadera separación de la memoria no se utilizó en los sistemas operativos de las computadoras domésticas hasta que se lanzó OS/2 (y en RISC OS ) en 1987. En sistemas anteriores, dicha falta de protección se utilizaba incluso como una forma de comunicación entre procesos , mediante el envío de un puntero entre procesos. Los procesos pueden acceder a la memoria del sistema en la familia de sistemas operativos Windows 9x . [12]
Algunos sistemas operativos que implementan protección de memoria incluyen:
En sistemas tipo Unix , la mprotect
llamada al sistema se utiliza para controlar la protección de la memoria. [14]