El problema fundamental que aborda este análisis es la divergencia entre el modelo de ejecución abstracto del lenguaje C y la implementación concreta generada por los compiladores optimizadores. Los programadores escriben código asumiendo un comportamiento secuencial y predecible, especialmente en lo que respecta a las lecturas de memoria. Sin embargo, los compiladores, en su búsqueda de optimización, pueden introducir lecturas adicionales de memoria ('invented loads') que no corresponden directamente a una instrucción explícita en el código fuente. Estas cargas inventadas son legalmente válidas bajo la máquina abstracta de C, que asume que la memoria no cambia entre lecturas, pero se convierten en un vector de ataque crítico cuando la memoria es compartida y modificable por un atacante.

La relevancia de este problema radica en su capacidad para subvertir patrones de seguridad bien establecidos, como el 'snapshotting' para prevenir vulnerabilidades TOCTOU. Un programador puede copiar datos de una fuente no confiable a una variable local para asegurar su inmutabilidad, pero una carga inventada por el compilador puede re-leer el dato original, ahora modificado por un atacante, anulando la protección. Esto crea una 'vulnerabilidad en superposición' (Schrödinger's TOCTOU), donde la seguridad del código fuente es indeterminada hasta el momento de la compilación, dependiendo de la versión del compilador, la arquitectura y las banderas de optimización.

Arquitectura del Sistema

La vulnerabilidad de las cargas inventadas surge de la compleja arquitectura de un compilador moderno, que típicamente incluye varias etapas: frontend (parsing, análisis semántico), lowering a una representación intermedia (IR), optimizaciones en el IR, asignación de registros y generación de código (backend). No hay una única etapa responsable; en cambio, es una propiedad emergente de la interacción no lineal de estas etapas. Por ejemplo, la 'rematerialización' puede recomputar un valor en lugar de cargarlo de un registro, o la 'fusión de operaciones de memoria CISC' puede combinar múltiples accesos.

El mecanismo central de la vulnerabilidad es la re-lectura de una ubicación de memoria que el programador creía que solo se había leído una vez y luego se había 'congelado' en una variable local. Esta re-lectura puede ocurrir entre un 'check' de seguridad y un 'use' posterior del valor. El artículo identifica varios mecanismos de compilador que pueden generar estas cargas inventadas, como 'rematerialization', 'width-mismatch reload', 'bulk-vs-scalar overlap', 'cross-class reload', 'CISC mem-op fold' y 'byte-order reload'. Estos mecanismos, aunque individualmente válidos para la optimización, pueden combinarse para reintroducir ventanas TOCTOU en código que explícitamente intenta prevenirlas mediante el patrón de 'snapshotting' (copiar a una variable local, validar la copia, usar la copia).

Flujo de Explotación TOCTOU con Carga Inventada

  1. 1 Programador: Snapshot Copia datos no confiables (ej. shared->len) a una variable local (local.len) ...
  2. 2 Programador: Check Valida local.len (ej. local.len <= 20).
  3. 3 Atacante: Race Condition Modifica shared->len en la memoria compartida.
  4. 4 Compilador: Carga Inventada El compilador re-lee shared->len (ahora modificado) para una operación poster...
  5. 5 Programador: Use Utiliza el valor re-leído (ahora malicioso) para una operación crítica (ej. m...
  6. 6 Resultado: Vulnerabilidad Desbordamiento de búfer o escalada de privilegios debido al valor malicioso.
struct message { int len; char data[20]; };
struct message slot;
char out[20];
void receive(struct message *shared) {
  struct message local = *shared; /* 1. snapshot untrusted input */
  if (local.len <= 20) /* 2. validate the snapshot */
    slot = local; /* 3. publish the validated copy */
}
void forward(void) {
  memcpy(out, slot.data, slot.len); /* slot.len was checked <= 20 ... right? */
}
Código C que intenta prevenir TOCTOU mediante la copia de datos a una variable local antes de la validación y uso. El compilador puede reintroducir la vulnerabilidad.

Fundamentos Teóricos

Este problema se conecta directamente con los fundamentos de la teoría de compiladores y la semántica de los lenguajes de programación, particularmente la máquina abstracta de C. La máquina abstracta de C define el comportamiento observable de un programa, pero otorga a los compiladores una considerable libertad para optimizar el código siempre que el resultado final sea semánticamente equivalente bajo ciertas suposiciones. Una de estas suposiciones es que la memoria no cambia arbitrariamente entre accesos, lo cual es violado en entornos con memoria compartida y atacantes concurrentes.

Aunque no se cita un paper específico que prediga este problema exacto, la tensión entre la optimización agresiva y la seguridad ha sido un tema recurrente en la investigación de seguridad de sistemas y compiladores. Conceptos como la 'aliasing analysis' y la 'data-flow analysis' son fundamentales para que los compiladores tomen decisiones de optimización. Sin embargo, en el contexto de memoria compartida y acceso no determinista (como en TOCTOU), estas análisis pueden ser insuficientes o basarse en modelos que no capturan el comportamiento malicioso. La discusión sobre 'memory models' en lenguajes concurrentes (como C11 atomics) intenta formalizar estas interacciones, pero el problema de las cargas inventadas demuestra que incluso el código secuencial puede ser afectado por interacciones inesperadas con la memoria subyacente y el optimizador.