La seguridad de memoria en lenguajes de bajo nivel como C y C++ sigue siendo un desafío fundamental en la computación, a pesar de décadas de investigación y desarrollo. Los errores de puntero, como los accesos fuera de límites o el uso después de liberar (use-after-free), son una fuente persistente de vulnerabilidades de seguridad y fallos de programa. InvisiCaps aborda este problema introduciendo un sistema de capacidades dinámico para punteros, que adjunta metadatos de seguridad a cada puntero sin alterar su tamaño visible de 64 bits.

Este enfoque es crítico en el panorama actual de sistemas distribuidos y seguridad, donde las vulnerabilidades de memoria pueden ser explotadas para comprometer sistemas enteros. La dificultad radica en lograr esta seguridad sin sacrificar la compatibilidad con el vasto ecosistema de código C/C++ existente y sin imponer una penalización de rendimiento inaceptable. InvisiCaps busca resolver este dilema, permitiendo que el código C/C++ se ejecute con garantías de seguridad de memoria que tradicionalmente solo se encuentran en lenguajes con recolección de basura o verificadores de tipos más estrictos.

Arquitectura del Sistema

InvisiCaps opera con una distinción clave entre 'flight pointers' (punteros en variables locales) y 'rest pointers' (punteros almacenados en el heap). Un 'flight pointer' consta de dos partes: un 'lower bound ptr' (o 'lower') y un 'intval'. El 'intval' es el valor entero del puntero visible para el programa C, mientras que el 'lower' es un puntero de confianza del runtime de Fil-C que apunta a la cabecera del objeto, conteniendo metadatos de capacidad.

Cuando un puntero se almacena en el heap ('rest pointer'), su 'intval' se guarda en el payload del objeto, y su 'lower' se almacena en una 'aux allocation' separada. Esta 'aux allocation' es referenciada por una 'aux word' en la cabecera del objeto, a la que apunta el 'lower' del 'flight pointer'. Esta separación asegura que el programa C solo vea el 'intval' de 64 bits, manteniendo la compatibilidad. Los accesos a memoria implican una verificación dinámica del 'intval' contra los límites definidos por el 'lower' y los metadatos en la cabecera del objeto y la 'aux allocation'.

Para punteros atómicos, la 'aux allocation' puede contener 'atomic box pointers' que apuntan a 'atomic boxes' de 16 bytes. Estas 'atomic boxes' contienen el 'flight pointer' completo y se manipulan usando operaciones atómicas de 128 bits (como double-CAS en X86_64 y ARM64). El sistema también gestiona tipos especiales como punteros a funciones, objetos de hilos (zthread) y objetos liberados, utilizando flags adicionales en la 'aux word' para aplicar políticas de acceso específicas y garantizar panics deterministas en caso de uso después de liberar.

Acceso a Puntero en Vuelo (Flight Pointer Access)

  1. 1 Programa C Intenta acceder a memoria usando un 'intval' (valor de puntero visible)
  2. 2 Runtime Fil-C Recupera el 'lower' asociado al 'flight pointer'
  3. 3 Runtime Fil-C Usa 'lower' para acceder a la cabecera del objeto (incluye 'aux word')
  4. 4 Runtime Fil-C Realiza verificación de límites ('intval' vs. 'lower'/'upper bound ptr')
  5. 5 Runtime Fil-C Verifica flags en 'aux word' (ej. objeto liberado, función, zthread)
  6. 6 Acceso a Memoria Si todas las verificaciones pasan, se permite el acceso
  7. 7 Panic Fil-C Si falla alguna verificación, el programa entra en pánico

Almacenamiento de Puntero en Reposo (Rest Pointer Storage)

  1. 1 Programa C Almacena un 'flight pointer' en el heap
  2. 2 Runtime Fil-C Extrae 'intval' y 'lower' del 'flight pointer'
  3. 3 Runtime Fil-C Almacena 'intval' en el payload del objeto en el heap
  4. 4 Runtime Fil-C Verifica si existe 'aux allocation' para el objeto
  5. 5 Runtime Fil-C Si no existe, crea 'aux allocation' y actualiza 'aux word' en cabecera
  6. 6 Runtime Fil-C Almacena 'lower' en la 'aux allocation' correspondiente
CapaTecnologíaJustificación
storage Heap Memory Almacena los 'intval' de los punteros y los payloads de los objetos. También aloja las 'aux allocations' que guardan los 'lower' de los punteros en reposo.
compute Fil-C Runtime Componente central que intercepta y valida todas las operaciones de puntero, gestiona los metadatos de capacidad y ejecuta las verificaciones de seguridad en tiempo de ejecución.
data-processing Garbage Collector (Fil's Unbelievable GC) Gestiona la memoria, especialmente las 'aux allocations'. Es crucial para el modelo de MonoCaps e InvisiCaps, ya que la 'aux word' permite al GC identificar objetos con punteros salientes.

Trade-offs

Ganancias
  • Seguridad de Memoria
  • Compatibilidad con C/C++ (punteros de 64 bits, idioms)
  • Rendimiento (vs. modelos anteriores)
  • Soporte para unions y tipos cambiantes
  • Thread Safety
Costes
  • Overhead de Rendimiento (vs. C nativo)
  • Uso de Memoria (para metadatos y aux allocations)
  • Complejidad del Runtime

Fundamentos Teóricos

El concepto de capacidades para punteros tiene profundas raíces en la investigación de seguridad de memoria y arquitecturas de computadoras. InvisiCaps se inspira fuertemente en SoftBound, un sistema que también coloca metadatos de capacidad 'fuera' del espacio de direcciones del programa para imponer límites de acceso. SoftBound, a su vez, se basa en ideas de verificación de punteros y seguridad de memoria que se remontan a trabajos como el de 'fat pointers' y sistemas de verificación en tiempo de ejecución.

Además, InvisiCaps puede verse como una implementación por software de los principios de CHERI (Capability Hardware Enhanced RISC Instructions), un proyecto que extiende las arquitecturas de conjunto de instrucciones para incluir capacidades de hardware. Mientras que CHERI busca la seguridad de memoria a través de extensiones de hardware con punteros de 128 o 256 bits, InvisiCaps logra un objetivo similar en software, manteniendo la compatibilidad con punteros de 64 bits. La gestión de 'use-after-free' y la protección de metadatos contra corrupción son problemas abordados en la literatura académica sobre sistemas operativos seguros y lenguajes de programación, buscando un equilibrio entre rendimiento, compatibilidad y robustez.