La tesis central de este trabajo es que la adaptación de lenguajes de programación de sistemas como Rust a arquitecturas de seguridad de memoria avanzada, como CHERIoT, requiere una reevaluación fundamental de las suposiciones del compilador sobre la representación y el comportamiento de los punteros. Tradicionalmente, los compiladores asumen que el tamaño de un puntero es idéntico al tamaño de una dirección de memoria, y que los punteros solo contienen información de dirección. Sin embargo, en arquitecturas basadas en capacidades (purecap), los punteros (o 'capacidades') encapsulan no solo una dirección, sino también metadatos de seguridad y permisos, lo que altera su tamaño y semántica.

Este problema se agudiza en CHERIoT, donde las direcciones son de 32 bits, pero las capacidades son de 64 bits, con los 32 bits adicionales dedicados a metadatos de seguridad. La necesidad de soportar estas arquitecturas surge de la creciente demanda de sistemas con garantías de seguridad de memoria más fuertes a nivel de hardware, mitigando clases enteras de vulnerabilidades como los buffer overflows y los use-after-free, que persisten incluso con lenguajes como Rust que ofrecen seguridad de memoria a nivel de software. La integración de Rust con CHERIoT busca combinar la seguridad de memoria de Rust con las garantías de hardware de CHERI, ofreciendo una pila de software robusta para sistemas embebidos y críticos.

Arquitectura del Sistema

La adaptación de Rust a CHERIoT implica modificaciones en varias capas de la pila de compilación. En la base, se actualiza el submódulo LLVM para apuntar a una versión de LLVM con soporte para CHERIoT. Esta versión modificada de LLVM introduce cambios en las firmas de funciones C para operaciones como memcpy y memmove, que ahora deben considerar si los datos copiados son capacidades y si sus metadatos deben preservarse. LLVM también se configura para usar el espacio de direcciones 200 por defecto para capacidades, en lugar del espacio 0 tradicional, reflejando la naturaleza purecap de CHERIoT.

El compilador Rust (rustc) se modifica para reconocer y utilizar este nuevo target (riscv32cheriot-unknown-cheriotrtos). Un cambio crucial es la distinción entre pointer_size y pointer_offset en la data_layout del target. Mientras que pointer_size se refiere al tamaño total de la capacidad (64 bits en CHERIoT), pointer_offset se refiere al tamaño de la parte de la dirección (32 bits). Esta distinción es vital para funciones que operan con índices o calculan rangos de memoria, que deben usar el tamaño de la dirección efectiva. Además, se añaden intrinsics específicos de CHERI y se adaptan las bibliotecas core y alloc para compilar y funcionar correctamente en este entorno, incluyendo el soporte para AtomicPtr de 64 bits en una plataforma de 32 bits. La integración continua (CI) se extiende para construir core y ejecutar tests codegen-llvm y tests de ejecución en el simulador Sail para CHERIoT, validando la corrección del código generado.

Flujo de Compilación de Rust para CHERIoT

  1. 1 Código Fuente Rust Aplicación o biblioteca Rust
  2. 2 rustc Compilador Rust, configurado para target `riscv32cheriot-unknown-cheriotrtos`
  3. 3 LLVM (CHERIoT Port) Backend de LLVM modificado para manejar capacidades y espacios de direcciones...
  4. 4 Generación de IR LLVM IR con punteros en `addrspace(200)` y semánticas de capacidad
  5. 5 Generación de Ensamblador Código ensamblador RISC-V con instrucciones CHERI
  6. 6 Enlazado Creación de binario ejecutable CHERIoT
  7. 7 Simulador Sail/Hardware Ejecución y verificación del código en entorno CHERIoT
CapaTecnologíaJustificación
compute Rust Compiler (rustc) Adaptación del frontend y mid-end del compilador para entender y generar código para la arquitectura CHERIoT, incluyendo la distinción entre tamaño de puntero y dirección. `--target=riscv32cheriot-unknown-cheriotrtos`
compute LLVM (CHERIoT Port) Backend de compilación que traduce el IR a código máquina RISC-V con extensiones CHERI, manejando las semánticas de capacidad y los espacios de direcciones específicos. Uso de `addrspace(200)` para capacidades; funciones `memcpy`/`memmove` con conciencia de capacidades.
storage CHERIoT Platform Arquitectura de hardware de seguridad de memoria basada en capacidades, donde los punteros (capacidades) incluyen metadatos de seguridad. Arquitectura purecap: direcciones de 32 bits, capacidades de 64 bits.
orchestration CI/CD Pipeline Automatización de la construcción y ejecución de tests para el target CHERIoT, incluyendo `codegen-llvm` y ejecución en simulador. Pasos para construir `core` y ejecutar tests en simulador Sail.
fn int_ty_max(&self, int_ty: IntTy) -> u128 {
    match int_ty {
        IntTy::Isize => self.tcx.data_layout.pointer_offset().signed_int_max()
        // ...
    }
}
Ejemplo de cómo el compilador Rust accede a la configuración de `data_layout` para determinar el tamaño máximo de un tipo entero basado en el `pointer_offset` de la arquitectura, en lugar del `pointer_size`.
; if let Some(prefix) = prefix { f.buf.write_str(prefix) } else { Ok(()) }
80017f68: ce81 beqz a3, 0x80017f80 <core::fmt::Formatter>::pad_integral::write_prefix+0x68>
80017f6a: 6d84 ct.clc s1, 0x18(a1)
80017f6c: fea7855b ct.cmove a0, a5
80017f70: fea685db ct.cmove a1, a3
80017f74: 863a mv a2, a4
80017f76: 70a2 ct.clc ra, 0x28(sp)
80017f78: 7402 ct.clc s0, 0x20(sp)
80017f7a: 64e2 ct.clc s1, 0x18(sp)
80017f7c: 6145 ct.cincoffset sp, sp, 0x30
80017f7e: 8482 ct.cjr s1
80017f80: 4501 li a0, 0x0
Fragmento de ensamblador RISC-V que muestra un bug en CHERIoT-LLVM donde un registro callee-saved (`s1`) es sobrescrito antes de un `ct.cjr` (jump and link register capability), causando un `PermitExecuteViolation`.

Fundamentos Teóricos

El concepto de capacidades de hardware, central en CHERIoT, tiene profundas raíces académicas que se remontan a las décadas de 1960 y 1970. El trabajo pionero de Dennis y Van Horn en 1966 con el sistema MULTICS introdujo la idea de 'capacidades' como un mecanismo para el control de acceso y la protección de memoria. Posteriormente, sistemas como el Plessey System 250 y el Cambridge CAP Computer exploraron la implementación de arquitecturas basadas en capacidades en hardware.

CHERI (Capability Hardware Enhanced RISC Instructions) es una encarnación moderna de estos principios, desarrollado por la Universidad de Cambridge. Se basa en la idea de que los punteros deben ser 'capabilities' que no solo apunten a una ubicación en memoria, sino que también contengan metadatos que definan su tipo, tamaño y permisos de acceso. Esto permite que el hardware haga cumplir la seguridad de memoria, previniendo directamente muchos tipos de ataques de corrupción de memoria. El problema de la 'provenance' de los punteros, es decir, el seguimiento de su origen y validez, es un área activa de investigación en la teoría de lenguajes de programación y compiladores, y el esfuerzo de 'strict-provenance' en Rust es un ejemplo de cómo los principios académicos se traducen en mejoras prácticas para la seguridad del software.