David Chisnall considera que el desafío más difícil del aislamiento de software no consiste en separar por completo dos cargas de trabajo, sino en aislar componentes que necesitan comunicarse y compartir datos. En una presentación publicada a través de InfoQ, Chisnall explica cómo la arquitectura CHERI combina conceptos de capacidades de seguridad y punteros ampliados para proporcionar protección de hardware a los punteros, junto con seguridad de memoria y aislamiento preciso dentro del mismo espacio de direcciones.
Chisnall es cofundador y director de ingeniería de sistemas de SCI Semiconductor, además de investigador visitante en la Universidad de Cambridge. La presentación se basa en los trabajos de investigación de CHERI y en sus implementaciones de software y hardware, desde CheriBSD y las extensiones ARM Morello hasta CHERIoT, orientado a microcontroladores, además de la base de RISC-V denominada RVY, cuyo trabajo de estandarización se encontraba, según afirmó, en sus etapas finales.
El puntero se convierte en una entidad protegida por hardware
CHERI no presenta un conjunto de instrucciones independiente en el sentido tradicional, sino un conjunto de ideas que puede adaptarse a distintas arquitecturas, de forma similar a cómo se aplican los conceptos de SIMD mediante múltiples extensiones. Según esta concepción, el puntero deja de ser simplemente un número que representa una dirección de memoria y se convierte en una capacidad que contiene la dirección, los límites del objeto y un conjunto de permisos, además de una etiqueta de hardware que determina si el valor es un puntero válido.
Estas capacidades imponen propiedades unidireccionales: los límites pueden estrecharse y los permisos pueden eliminarse, pero no pueden ampliarse ni restaurarse a partir del propio puntero. El hardware también verifica que el puntero sea válido y esté etiquetado antes de utilizarlo en operaciones de carga, almacenamiento o salto. Si un programa intenta utilizar un valor tratado como datos o manipulado para producir una dirección, la operación falla en lugar de convertirlo automáticamente en un puntero utilizable.
Este diseño proporciona, según la presentación, protección directa frente a varias etapas habituales de los ataques de explotación de memoria, como sobrescribir un puntero con un valor controlado por el atacante. También permite operaciones de copia generales como memcpy sin necesidad de conocer previamente las ubicaciones de los punteros, porque la etiqueta del puntero se transfiere junto con el valor o se elimina al sobrescribir parte del espacio de capacidad.
Límites y permisos que pueden reducirse
Las capacidades de CHERI incluyen permisos de lectura y escritura, así como permisos separados para manipular los punteros contenidos en los datos. Chisnall explica que esta precisión permite, por ejemplo, conceder a un programa permiso para leer los datos de una estructura compleja sin darle la capacidad de almacenar nuevos punteros en ella. De este modo, puede proporcionarse una vista de solo lectura que abarque un árbol de datos completo, en lugar de proteger superficialmente un único objeto.
El mecanismo de sellado también permite crear punteros opacos seguros, similares a identificadores o descriptores de archivo, pero sin que sea necesariamente necesario utilizar una tabla de traducción administrada por el núcleo. La capacidad se sella mediante un código de tipo, por lo que no puede modificarse, desreferenciarse ni utilizarse para saltar, cargar o almacenar mientras permanece sellada. Cuando se devuelve a la entidad propietaria, el desellado solo tiene éxito si el código de tipo coincide.
Seguridad espacial y temporal de la memoria
En el caso de errores de desbordamiento de límites, CHERI puede convertir un comportamiento indefinido del lenguaje C en una detención determinista del hardware. Chisnall muestra el ejemplo de una matriz de 14 elementos cuyo elemento 14 intenta ser accedido: en un sistema tradicional, el acceso podría sobrescribir la dirección de retorno, mientras que en un sistema CHERI la operación genera una excepción porque el puntero está restringido a los límites de la matriz.
La propuesta no se limita a la seguridad espacial. En CHERIoT, las etiquetas de los punteros y una memoria sombra que contiene un bit por unidad de asignación permiten invalidar los punteros asociados con memoria que ha sido liberada. Al cargar una capacidad en un registro, un filtro de carga puede comprobar el estado del bloque y eliminar la etiqueta si la memoria ha sido liberada. Un mecanismo de hardware periódico también examina la memoria y elimina las etiquetas de los punteros que apuntan a objetos que ya no están vivos, lo que permite reutilizar la memoria una vez completado el ciclo de invalidación.
De la seguridad de la memoria al aislamiento preciso
El objetivo original del proyecto CHERI, según lo expone Chisnall, era construir un aislamiento preciso que superara los límites del aislamiento tradicional de procesos. Aislar cada componente en un proceso independiente impone costes relacionados con las tablas de páginas, los mecanismos RPC y la serialización y copia de datos. Esto puede ser aceptable para decenas de dominios aislados, pero resulta menos adecuado cuando los navegadores, por ejemplo, necesitan ejecutar miles o decenas de miles de cajas de arena para componentes como la decodificación de imágenes y audio o la ejecución de JavaScript JIT.
El modelo CHERI permite considerar como dominio de protección el conjunto de memoria accesible a partir de las capacidades presentes en los registros de ejecución. Dos dominios dentro del mismo espacio de direcciones pueden compartir algunos datos mientras mantienen aisladas otras partes, porque la compartición comienza con capacidades específicas y puede extenderse a estructuras de datos complejas y a todo aquello que sea accesible desde ellas.
En sistemas más grandes, la presentación explica una transición gradual mediante la llamada coexecve, que crea un proceso nuevo sin crear un espacio de direcciones nuevo y después establece un conjunto de capacidades raíz que determina a qué puede acceder el nuevo proceso. CHERIoT, por su parte, adopta una separación estricta de permisos dentro de un sistema operativo en tiempo real, con un componente de conmutación responsable del cambio de contexto y de las llamadas entre compartimentos, cuyo tamaño es de aproximadamente 350 instrucciones, según la presentación.
La conclusión que plantea Chisnall es que la protección de la memoria y el aislamiento no deberían depender de reescribir por completo las reglas del código ni de separar cada componente en un proceso independiente. Mantener el modelo de C cercano al habitual, haciendo que los punteros y las capacidades de acceso sean comprensibles y estén controlados por el hardware, puede proporcionar una vía gradual para reducir los errores de memoria y construir una compartición más fácil de auditar. Los detalles de implementación siguen siendo diferentes entre los sistemas grandes y los microcontroladores, y la presentación refleja el punto de vista de su autor y su experiencia directa con CHERI, no un estándar general que garantice automáticamente la eliminación de todos los problemas de seguridad.