Memoria de solo ejecución (XOM) para archivos binarios AArch64

Las secciones de código ejecutable para los archivos binarios del sistema AArch64 están marcadas de forma predeterminada como de solo ejecución (no legible) como una mitigación más fuerte contra los ataques de reutilización de código justo a tiempo. El código que combina datos y código y el código que inspecciona deliberadamente estas secciones (sin volver a asignar primero los segmentos de memoria para que sean legibles) ya no funcionan. Las aplicaciones con un SDK objetivo de 10 (nivel de API 29 o superior) se ven afectadas si la aplicación intenta leer secciones de código de bibliotecas del sistema habilitadas para memoria de solo ejecución (XOM) en la memoria sin marcar primero la sección como legible.

Para beneficiarse completamente de esta mitigación, se requiere soporte tanto de hardware como de kernel. Sin este apoyo, es posible que la mitigación solo se aplique parcialmente. El kernel común de Android 4.9 contiene los parches apropiados para proporcionar soporte completo para esto en dispositivos ARMv8.2.

Implementación

Los binarios AArch64 generados por el compilador asumen que el código y los datos no están entremezclados. Habilitar esta función no afecta negativamente el rendimiento del dispositivo.

Para el código que tiene que realizar una introspección de memoria intencional en sus segmentos ejecutables, se recomienda llamar a mprotect en los segmentos de código que requieren inspección para permitir que sean legibles y luego eliminar la legibilidad cuando se complete la inspección.
Esta implementación hace que las lecturas en los segmentos de memoria marcados como de solo ejecución den como resultado un error de segmentación ( SEGFAULT ). Esto puede ocurrir como resultado de un error, vulnerabilidad, datos mezclados con código (agrupación literal) o introspección de memoria intencional.

Soporte e impacto del dispositivo

Es posible que los dispositivos con hardware anterior o kernels anteriores (inferiores a 4.9) sin los parches requeridos no sean completamente compatibles con esta función o no se beneficien de ella. Es posible que los dispositivos sin compatibilidad con el kernel no impongan el acceso de los usuarios a la memoria de solo ejecución; sin embargo, el código del kernel que verifica explícitamente si una página es legible aún puede aplicar esta propiedad, como process_vm_readv() .

El indicador del kernel CONFIG_ARM64_UAO debe configurarse en el kernel para garantizar que el kernel respete las páginas de usuario marcadas como de solo ejecución. Es posible que los dispositivos ARMv8 anteriores, o los dispositivos ARMv8.2 con la anulación de acceso de usuario (UAO) deshabilitada, no se beneficien completamente de esto y aún puedan leer páginas de solo ejecución mediante llamadas al sistema.

Refactorización de código existente

El código que se ha portado desde AArch32 puede contener datos y código entremezclados, lo que provoca que surjan problemas. En muchos casos, solucionar estos problemas es tan simple como mover las constantes a una sección .data en el archivo de ensamblaje.

Es posible que sea necesario refactorizar el ensamblaje escrito a mano para separar las constantes agrupadas localmente.

Ejemplos:

Los archivos binarios generados por el compilador de Clang no deberían tener problemas con la mezcla de datos en el código. Si se incluye código generado por la colección de compiladores GNU (GCC) (de una biblioteca estática), inspeccione el binario de salida para asegurarse de que las constantes no se hayan agrupado en secciones de código.

Si es necesaria la introspección del código en las secciones del código ejecutable, primero llame a mprotect para marcar el código como legible. Luego, una vez completada la operación, vuelva a llamar a mprotect para marcarlo como ilegible.

Habilitación

Solo ejecutar está habilitado de forma predeterminada para todos los archivos binarios de 64 bits en el sistema de compilación.

Deshabilitar

Puede deshabilitar ejecutar solo a nivel de módulo, por un árbol de subdirectorios completo o globalmente para una compilación completa.

XOM se puede deshabilitar para módulos individuales que no se pueden refactorizar, o que necesitan leer su código ejecutable, configurando las variables LOCAL_XOM y xom en false .

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

Si la memoria de solo ejecución está deshabilitada en una biblioteca estática, el sistema de compilación aplica esto a todos los módulos dependientes de esa biblioteca estática. Puede anular esto usando xom: true, .

Para deshabilitar la memoria de solo ejecución en un subdirectorio particular (por ejemplo, foo/bar/), pase el valor a XOM_EXCLUDE_PATHS .

make -j XOM_EXCLUDE_PATHS=foo/bar

Alternativamente, puede establecer la variable PRODUCT_XOM_EXCLUDE_PATHS en la configuración de su producto.

Puede deshabilitar los binarios de solo ejecución globalmente pasando ENABLE_XOM=false a su comando make .

make -j ENABLE_XOM=false

Validación

No hay CTS ni pruebas de verificación disponibles para la memoria de solo ejecución. Puede verificar manualmente los binarios usando readelf y verificando las banderas de segmento.