jueves, 18 de diciembre de 2008

Protección

En el modo protegido, la IA-32 y Intel 64 proporcionan un mecanismo de protección que opera en el nivel de segmentación y en el de paginamiento. Este mecanismo permite limitar el acceso a ciertos segmentos o páginas basándose en los niveles de privilegios (4 niveles de privilegios para segmentos, 2 para páginas). La protección por segmentación o por paginación puede ser usada en todos los niveles del entorno de desarrollo del software para ayudar en la localización y la detección de problemas de diseño y de errores. También puede ser incorporado en productos finales para ofrecer más robustez a los sistemas operativo, utilidades de software, y aplicaciones de software. Cuando el mecanismo de protección está activo, cada referencia a la memoria es revisada para verificar que satisface varias revisiones de protección. Todas las revisiones se hacen antes de que empiece el ciclo de memoria; ningún error se convierte en excepción. Como las revisiones son ejecutadas en paralelo con la traducción de direcciones, no hay ningún fallo del rendimiento. Los tipos de revisiones son: revisiones de los límites, revisiones de los tipos, revisiones de los niveles de privilegios, restricciones del dominio direccionable, restricciones del procedimiento de entry-points, restricciones del conjunto de instrucciones. Todas los errores de protección resultan en generar una excepción.
Para activar el modo protegido, se debe activar la bandera PE del registro CR0, que activa automáticamente el mecanismo de protección de segmentos. Una vez en modo protegido, no hay bit de control para desactivarlo o activarlo. La parte de mecanismo de protección de segmentos está basado en los niveles de privilegios, y la forma de desactivarlo es dando a todos los selectores y descriptores de segmentos los máximos privilegios (nivel 0). Esto desactiva todas las barreras entre segmentos, pero las otras revisiones de protección (como revisiones de límites, de tipos...) se llevan a cabo igual. La protección del nivel de paginación se activa automáticamente cuando se activa la paginación, y tampoco hay bit de control. Sin embargo, se puede desactivar de dos formas: poniendo a cero la bandera WP del registro de control CR0, o poniendo a uno la bandera de lectura/escritura y la de usar/supervisar para cada entrada del directorio de paginación y de la tabla de paginación. Esto permite que se pueda escribir y usar la página, y por lo tanto acaba con la protección del nivel de paginación.
El campo límite de un descriptor de segmentos previene a los programas o procedimientos de direccionar posiciones de memoria que estén fuera del segmento. El valor efectivo del límite depende de la bandera G (granulación). Para segmentos de datos, el límite depende de las banderas E (dirección de Expansión) y B. Si la bandera G está inactiva, el límite efectivo es el valor del límite del campo de 20 bits en el descriptor de segmento. Aquí, el rango es de 0 a FFFFFH (1 MByte). Si está activada, el procesador escala el valor del límite del campo en un factor de 2^12 (4 KBytes), por lo tanto el rango del límite efectivo es de FFFH (4 KBytes) a FFFFFFFFH (4 GBytes). Cuando se usa este escalado, a los 12 bits más bajos del offset de un segmento no se les aplica la revisión de límites. En el modo de 64 bits, el procesador no realiza las comprobaciones de límites de los segmentos de código o de datos en tiempo de ejecución. Sin embargo, realiza comprobaciones de los límites de las tablas de descriptores.
Los descriptores de segmento contienen información de tipo en dos lugares: en la bandera S, y en el campo de tipo. El procesador usa esta información para detectar errores en la programación que resultan en un intento de usar un segmento o puerta incorrecto o de una forma no prevista. La bandera S indica si el descriptor es un tipo de sistema, o si es un tipo de código o dato. El campo de tipo proporciona 4 bits adicionales para usarlos en definir varios tipos de descriptores de código, datos y sistema. El procesador examina la información de tipo varias veces cuando se opera con selectores y descriptores de segmento.
El mecanismo de protección de segmentos del procesador reconoce 4 niveles de privilegios numerados de 0 a 3 (0 máximos privilegios, 3 mínimos). Normalmente lo único que tiene privilegios 0 es el código crítico, como el núcleo (kernel) de un sistema operativo. El procesador usa este mecanismo para evitar que un programa con privilegios bajos acceda a un segmento que requiere privilegios mayores, excepto en situaciones controladas. Si detecta que se está intentando acceder a un segmento sin los privilegios adecuados, el procesador generará una excepción.
Para acceder a los operandos en un segmento de datos, el selector de segmento de ese segmento tiene que ser cargado en los registros de segmento de dato (DS, ES, FS, o GS) o en el registro de segmento de pila (SS). Antes de cargar un selector de segmento en un registro de segmento, se hace una comprobación de privilegios comparando el nivel de privilegios del programa en ejecución, el RPL del selector de segmento, y el DPL del descriptor de segmento del segmento. El procesador carga el selector en el registro de segmento si el DPL es numéricamente superior o igual al CPL y al RPL. Si no, se genera una falta general de protección y el registro del segmento no se carga.
Para transferir el control del programa de un segmento de código a otro, el selector de segmento del segmento de destino se tiene que cargar en el registro de código de segmento (CS). Como parte del proceso de carga, se examina el descriptor de segmento del segmento de código de destino y hay comprobaciones de privilegios. Si estas comprobaciones son correctas se carga el registro CS, y el control del programa pasa al segmento de código nuevo, y la ejecución del programa comienza en la instrucción indicada por el registro EIP. Para administrar control de acceso a segmentos de código con diferentes niveles de privilegios, el procesador proporciona un conjunto especial de descriptores llamados descriptores de puertas. Hay 4 tipos: de puertas de llamada (facilitan transferencias de control entre distintos niveles de privilegios), de puertas trampa, de puertas de interrupciones, y de puertas de tareas. Para retornar de una llamada a un procedimiento se usa RET. Esta instrucción está prevista para ejecutarse cuando se llama a un procedimiento con la instrucción CALL ya que guarda un puntero en la pila que indica la instrucción de retorno, sin embargo JMP salta pero no guarda ningún puntero de retorno. Las instrucciones SYSENTER y SYSEXIT fueron incluidas en la arquitectura IA-32 en los Pentium II con el propósito de acelerar las llamadas al sistema operativo o a los procedimientos de ejecución. Con SYSENTER se puede acceder al sistema operativo o ejecutar procedimientos en privilegios de nivel 0, teniendo inicialmente los mínimos privilegios (nivel 3). SYSEXIT es una instrucción prevista para usar con privilegios nivel 0, para acelerar el retorno al código de nivel 3 de privilegios. SYSENTER se puede invocar desde los 4 niveles de privilegios, pero SYSEXIT sólo desde el nivel 0. Cuando el procesador está en modo protegido, valida todos los punteros para asegurar la protección entre segmentos y para mantener el aislamiento entre los niveles de privilegios. La validación de los punteros consiste en una serie de comprobaciones: Comprueba los derechos de acceso para determinar si el tipo de segmento es compatible con su uso, comprueba los derechos de lectura/escritura, comprueba si el offset del puntero excede el límite del segmento, comprueba si el proveedor del puntero tiene privilegios para acceder al segmento, y comprueba la alineación del offset. El procesador ejecuta automáticamente las 3 primeras comprobaciones durante la ejecución de las instrucciones. La cuarta se tiene que pedir por software, y la quinta se activa automáticamente cuando en el nivel 3 se activan las comprobaciones de alineación. Esto no afecta al aislamiento entre los niveles de privilegios.
La protección en el nivel de páginas se puede usar sola o aplicada a los segmentos. Cuando se usa esta protección (la de páginas) con modelo de memoria plano (sin segmentación), permite proteger al código supervisor y a sus datos (sistema operativo) del código de usuario y de sus datos (aplicaciones). Cuando se combinan la protección en nivel de páginas con la de nivel de segmentos, la protección de lectura/escritura del nivel de páginas proporciona una mayor granularidad de protección dentro de los segmentos. Con la protección en el nivel de paginación, en cada referencia a memoria se verifica que las comprobaciones de protección sean correctas. Todas estas comprobaciones se hacen antes de que empiece el ciclo de memoria, y cualquier fallo puede provocar que no comience el ciclo, y generar una excepción de fallo de página. La información de protección para páginas está en dos banderas en un directorio de paginación o en una entrada de la tabla de paginación: la bandera de lectura/escritura (bit 1) y la bandera de usuario/supervisor (bit 2). Las comprobaciones de protección se aplican a las tablas de paginación de primer y segundo nivel. El mecanismo de protección proporciona acceso restringido a páginas basado en 2 niveles de privilegios: modo supervisor (máximos privilegios, para el sistema operativo, datos protegidos...), y modo usuario (mínimos privilegios, para aplicaciones y datos). También reconoce dos tipos de páginas: las de acceso para sólo lectura, y las de acceso para lectura/escritura.
Cuando la paginación está activada, el procesador evalúa la protección de segmentación primero y después la de paginación. Si detecta un fallo en cualquiera de las comprobaciones, el acceso a memoria no se lleva a cabo y se genera una excepción. Las protecciones del nivel de páginas no pueden ser usadas para invalidar las de nivel de segmentos.
Además de la protección de nivel de páginas ofrecido por las banderas U/S y R/W, el aumento de las estructuras de paginación PAE proporcionan el bit de execute-disable. Este bit ofrece una protección adicional a las páginas de datos. Un procesador Intel 64 o un IA-32 con la capacidad del bit de desactivación de ejecución puede prevenir páginas de datos de ser usados por software malicioso que ejecuta código. Esta capacidad se ofrece en el modo 32 bits con PAE activado, y en el modo IA-32e. Mientras que la capacidad de ese bit no introduce nuevas instrucciones, requiere sistemas operativos para usar un entorno con PAE activado y para establecer una política de protección granular de páginas en las páginas de la memoria. Si el bit está activado, esa página puede ser usada sólo como datos. Un intento de ejecución de código de una página en memoria con el bit activo causa una excepción de fallo de la página. Sólo las páginas de 4 KBytes y las de 2 MBytes son soportadas por el bit de desactivación de paginación. Existen otros mecanismos de protección de nivel de paginación que se pueden aplicar a páginas en memoria independientemente de ese bit. Para detectar si un procesador tiene la capacidad del bit de desactivación de ejecución, se usa la instrucción CPUID (CPUID.80000001H. EDX[bit 20] = 1 indica que el bit está disponible). Cuando el bit está activado (IA32_EFER.NXE = 1), las condiciones para que ocurra un fallo en una página incluye las mismas condiciones que se aplica a un Intel 64 o a un IA-32 sin el bit activo, y añade una nueva: una instrucción para obtener una dirección lineal que traduzca direcciones físicas en páginas de memoria con el bit de desactivación de ejecución activado.

No hay comentarios: