
Asignador reforzado diseñado para sistemas modernos. Está integrado en la libc Bionic de Android y puede usarse externamente con musl y glibc como biblioteca dinámica para su uso en otras plataformas basadas en Linux. Irá adquiriendo mayor portabilidad e integración con el tiempo.
Este es un asignador de memoria de propósito general centrado en la seguridad que proporciona la API de malloc junto con diversas extensiones. Ofrece un refuerzo sustancial frente a vulnerabilidades de corrupción del heap. El diseño centrado en la seguridad también conduce a una sobrecarga de metadatos y a un desperdicio de memoria por fragmentación mucho menores que los de un diseño de asignador más tradicional. Su objetivo es proporcionar un rendimiento general decente con un enfoque en el rendimiento y el uso de memoria a largo plazo en lugar de en micro-benchmarks del asignador. Ofrece escalabilidad mediante un número configurable de arenas completamente independientes, con el bloqueo interno dentro de las arenas dividido además por clase de tamaño.
Este proyecto actualmente soporta Bionic (Android), musl y glibc. Puede que soporte otros sistemas operativos no basados en Linux en el futuro. Para Android, existe una integración personalizada y otras características de refuerzo que también están planificadas para musl en el futuro. El soporte de glibc se limitará a reemplazar la implementación de malloc, ya que musl es una base mucho más robusta y limpia sobre la que construir y puede cubrir los mismos casos de uso.
Este asignador está pensado como sucesor de una implementación anterior basada en extender OpenBSD malloc con diversas características de seguridad adicionales. Sigue estando fuertemente basado en el diseño de OpenBSD malloc, aunque no en el código existente, salvo por la reutilización de la implementación de la tabla hash. Las principales diferencias en el diseño son que se centra únicamente en el refuerzo en lugar de en la búsqueda de errores, utiliza clases de tamaño más finas junto con tamaños de slab que superan los 4k para reducir la fragmentación interna, no depende de que el kernel tenga una aleatorización de mmap de grano fino y solo apunta a 64 bits para hacer un uso agresivo del amplio espacio de direcciones. Hay muchas diferencias más pequeñas en el enfoque de implementación. Incorpora las extensiones anteriores realizadas a OpenBSD malloc, incluyendo la adición de relleno a las asignaciones para canarios (distintos de los canarios actuales de OpenBSD malloc), la detección de escritura tras liberación vinculada al borrado existente al liberar, colas junto a los arrays aleatorizados existentes para poner en cuarentena las asignaciones y una detección adecuada de doble liberación para las asignaciones en cuarentena. Las regiones de memoria por clase de tamaño con sus propias bases aleatorias se inspiraron libremente en la partición basada en tamaño y tipo de PartitionAlloc. Los cambios planificados a OpenBSD malloc resultaron ser demasiado extensos e invasivos, por lo que este proyecto se inició como una implementación nueva capaz de lograr mejor los objetivos. Para 32 bits, se puede usar en su lugar un port de OpenBSD malloc con pequeñas extensiones, ya que este asignador fundamentalmente no soporta ese entorno.
Debian stable (actualmente Debian 13) determina el conjunto más antiguo de dependencias soportadas:
Para Android, se soportan las ramas Linux GKI 6.1, 6.6 y 6.12.
Sin embargo, se recomienda encarecidamente usar versiones más recientes. Las versiones anteriores de las dependencias pueden ser compatibles en este momento, pero no están probadas y explícitamente no estarán soportadas.
Para el reemplazo externo de malloc con musl, se requiere musl 1.1.20. Sin embargo, habrá una integración personalizada que ofrecerá un mejor rendimiento en el futuro junto con otro endurecimiento para la implementación de la biblioteca estándar de C.
Para Android, solo se admitirá la generación actual, la rama de mantenimiento en desarrollo activo del
Proyecto de Código Abierto de Android, lo que actualmente significa android17-release.
El script preload.sh se puede utilizar para probar con ejecutables enlazados dinámicamente
que usen glibc o musl:
./preload.sh krita --new-image RGBA,U8,500,500
Puede ser necesario aumentar sustancialmente el sysctl vm.max_map_count para
acomodar la gran cantidad de mapeos causados por los guard slabs y las grandes
regiones de guarda de asignación. El número de mapeos también se puede reducir drásticamente
mediante un aumento significativo de CONFIG_GUARD_SLABS_INTERVAL, pero la
característica tiene un bajo costo de rendimiento y uso de memoria, por lo que no se recomienda.
Puede ofrecer un rendimiento ligeramente mejor cuando se integra en la biblioteca estándar de C y existen otras oportunidades para un endurecimiento similar dentro de las implementaciones de la biblioteca estándar de C y del enlazador dinámico. Por ejemplo, se puede implementar una región de biblioteca para ofrecer un aislamiento similar para las bibliotecas dinámicas como el que ofrece este asignador entre las distintas clases de tamaño. La intención es que esto se ofrezca como parte de variantes endurecidas de las bibliotecas estándar de C Bionic y musl.
Se proporciona una colección de pruebas automatizadas simples que se pueden ejecutar con el comando make de la siguiente manera:
make test
Se requiere OpenSSH 8.1 o superior para permitir las llamadas al sistema mprotect PROT_READ|PROT_WRITE
en el filtro seccomp-bpf en lugar de matar el proceso.
En GrapheneOS, hardened_malloc está integrado en la biblioteca estándar de C como la implementación estándar de malloc. Otros sistemas operativos basados en Android pueden reutilizar el código de integración para proporcionarlo. Si se desea, jemalloc puede dejarse como una opción de configuración en tiempo de ejecución usando hardened_malloc solo de forma condicional para dar a los usuarios la elección entre rendimiento y seguridad. Sin embargo, esto reduce la seguridad para modelos de amenaza en los que el estado persistente no es de confianza, es decir, el arranque verificado y la atestación (consulte el proyecto hermano de atestación).
Asegúrese de aumentar también sustancialmente vm.max_map_count para acomodar el
gran número de páginas de guarda creadas por hardened_malloc. Esto se puede hacer en
init.rc (system/core/rootdir/init.rc) cerca de la otra configuración de memoria virtual:
write /proc/sys/vm/max_map_count 1048576
Esto no es necesario si establece CONFIG_GUARD_SLABS_INTERVAL en un valor muy grande
en la configuración de compilación.