Revista Informática

Verificación De Límites En Miembros De Matrices Flexibles

Publicado el 20 julio 2024 por Lauratuero @incubaweb

Las sobrecargas de búfer son la causa de muchos problemas de seguridad y son una preocupación constante para los programadores. El lenguaje C es particularmente vulnerable a estas sobrecargas. La aparición de sanitizadores ha mitigado algunos problemas de seguridad al insertar automáticamente comprobaciones de límites, pero no pueden hacerlo en todas las situaciones, especialmente con miembros de matrices flexibles, cuyo tamaño solo se conoce en tiempo de ejecución.

El tamaño de un miembro de matriz flexible es típicamente opaco para el compilador. El atributo alloc_size en malloc() puede utilizarse para la comprobación de límites de los miembros de matrices flexibles dentro de la misma función que la asignación. Sin embargo, la información del atributo no se lleva con el objeto asignado, lo que hace imposible realizar verificaciones de límites en otros lugares.

Para mitigar este inconveniente, Clang y GCC están introduciendo el atributo counted_by para los miembros de matrices flexibles.

El número de elementos asignados para un miembro de matriz flexible se almacena frecuentemente en otro campo dentro de la misma estructura. Cuando se aplica al miembro de la matriz flexible, el atributo counted_by es usado por el sanitizador—habilitado por -fsanitize=array-bounds—haciendo referencia explícita al campo que almacena el número de elementos. El atributo crea una relación implícita entre el miembro de la matriz flexible y el campo de conteo, lo que permite al sanitizador de límites de matriz verificar las operaciones de la matriz flexible.

Existen algunas reglas a seguir al usar esta característica. Para la siguiente estructura:

struct foo {
    /* ... */
    size_t count; /* Number of elements in array */
    int array[] __attribute__((counted_by(count)));
};
  • El campo count debe estar dentro de la misma estructura no anónima que el miembro de la matriz flexible.
  • El campo count debe ser establecido antes de cualquier acceso a la matriz.
  • El campo array debe tener al menos el número de elementos count disponibles en todo momento.
  • El campo count puede cambiar, pero nunca debe ser mayor que el número de elementos originalmente asignados.

Un ejemplo de asignación de la estructura anterior sería:

struct foo *foo_alloc(size_t count) {
  struct foo *ptr = NULL;
  size_t size = MAX(sizeof(struct foo),
                    offsetof(struct foo, array[0]) +
                    count * sizeof(p->array[0]));

  ptr = calloc(1, size);
  ptr->count = count;
  return ptr;
}

La fortificación (habilitada por el macro _FORTIFY_SOURCE) es un proyecto en curso para hacer el kernel de Linux más seguro. Su enfoque principal es prevenir sobrecargas de búfer en operaciones de memoria y cadenas.

La fortificación utiliza los built-in __builtin_object_size() y __builtin_dynamic_object_size() para intentar determinar si la entrada pasada a una función es válida (es decir, "segura"). Una llamada a __builtin_dynamic_object_size() generalmente no puede tener en cuenta el tamaño de un miembro de matriz flexible. Pero con el atributo counted_by, es posible calcular el tamaño y mejorar la seguridad.

El atributo counted_by ya se utiliza en el kernel de Linux y será esencial para detectar problemas como desbordamientos de enteros, que conducen a sobrecargas de búfer en el heap. Queremos expandir su uso a más miembros de matrices flexibles y hacer cumplir su uso en el futuro.

En conclusión, el atributo counted_by ayuda a abordar un obstáculo de fortificación de larga data donde los límites de memoria de un miembro de matriz flexible no podían ser determinados por el compilador, haciendo que Linux y otras aplicaciones robustas sean menos explotables.

Por Bill Wendling, Ingeniero de Software del Personal

vía: Google Blog Open Source


Volver a la Portada de Logo Paperblog