Rust en la plataforma Android


La corrección del código en la plataforma Android es una prioridad máxima para la seguridad, estabilidad y calidad de cada versión de Android. Los errores de seguridad de la memoria en C y C ++ siguen siendo la fuente de errores más difícil de solucionar. Invertimos una gran cantidad de esfuerzo y recursos para detectar, corregir y mitigar esta clase de errores, y estos esfuerzos son efectivos para evitar que una gran cantidad de errores se incluyan en las versiones de Android. Sin embargo, a pesar de estos esfuerzos, los errores de seguridad de la memoria continúan siendo uno de los principales contribuyentes a los problemas de estabilidad y representan constantemente ~70% de las vulnerabilidades de seguridad de alta gravedad de Android.

Además de en marcha y próximo esfuerzos para mejorar la detección de errores de memoria, estamos intensificando los esfuerzos para prevenirlos en primer lugar. Los lenguajes seguros para la memoria son el medio más rentable para prevenir errores de memoria. Además de los lenguajes seguros para la memoria como Kotlin y Java, nos complace anunciar que el Proyecto de código abierto de Android (AOSP) ahora es suitable con el lenguaje de programación Rust para desarrollar el sistema operativo en sí.

Los lenguajes administrados como Java y Kotlin son la mejor opción para el desarrollo de aplicaciones de Android. Estos lenguajes están diseñados para facilitar su uso, portabilidad y seguridad. El Tiempo de ejecución de Android (Artwork) gestiona la memoria en nombre del desarrollador. El sistema operativo Android utiliza Java de forma extensiva, lo que protege de forma eficaz gran parte de la plataforma Android de errores de memoria. Desafortunadamente, para las capas inferiores del sistema operativo, Java y Kotlin no son una opción.


Los niveles inferiores del sistema operativo requieren lenguajes de programación de sistemas como C, C ++ y Rust. Estos lenguajes están diseñados con el command y la previsibilidad como objetivos. Proporcionan acceso a hardware y recursos del sistema de bajo nivel. Son livianos en recursos y tienen características de rendimiento más predecibles.

Para C y C ++, el desarrollador es responsable de administrar la vida útil de la memoria. Desafortunadamente, es fácil cometer errores al hacer esto, especialmente en bases de código complejas y multiproceso.


Rust proporciona garantías de seguridad de la memoria mediante el uso de una combinación de comprobaciones en tiempo de compilación para hacer cumplir la vida útil / propiedad del objeto y las comprobaciones en tiempo de ejecución para garantizar que los accesos a la memoria sean válidos. Esta seguridad se logra al tiempo que proporciona un rendimiento equivalente a C y C ++.

Los lenguajes C y C ++ no ofrecen las mismas garantías de seguridad y requieren un aislamiento sólido. Todos los procesos de Android están en espacio aislado y seguimos las Regla de 2 para decidir si la funcionalidad requiere aislamiento y privación adicionales. La Regla de 2 es basic: dadas tres opciones, los desarrolladores solo pueden seleccionar dos de las siguientes tres opciones.

Para Android, esto significa que si el código está escrito en C / C ++ y analiza una entrada que no es de confianza, debe estar contenido dentro de una caja de arena estrictamente restringida y sin privilegios. Tiempo adherencia a la Regla de 2 ha sido eficaz para reducir la gravedad y la accesibilidad de las vulnerabilidades de seguridad, tiene limitaciones. El sandboxing es caro: los nuevos procesos que requiere consumir gastos generales adicionales e introducir latencia debido al IPC y al uso de memoria adicional. Sandboxing no elimina las vulnerabilidades del código y su eficacia se lower en alta densidad de errores, lo que permite a los atacantes encadenar múltiples vulnerabilidades.

Los lenguajes seguros para la memoria como Rust nos ayudan a superar estas limitaciones de dos formas:

  1. Lessen la densidad de errores dentro de nuestro código, lo que aumenta la efectividad de nuestro sandboxing precise.
  2. Cut down nuestras necesidades de sandboxing, lo que permite la introducción de nuevas funciones que son más seguras y ligeras en recursos.

Por supuesto, la introducción de un nuevo lenguaje de programación no soluciona errores en nuestro código C / C ++ existente. Incluso si redirigimos los esfuerzos de todos los ingenieros de software del equipo de Android, reescribir decenas de millones de líneas de código simplemente no es factible.

El análisis anterior de la period de los errores de seguridad de la memoria en Android (medidos desde el momento en que se introdujeron por primera vez) demuestra por qué nuestros esfuerzos de lenguaje seguro para la memoria se centran mejor en el nuevo desarrollo y no en la reescritura de código C / C ++ maduro. La mayoría de nuestros errores de memoria ocurren en código nuevo o modificado recientemente, y aproximadamente el 50% tiene menos de un año.

La rareza comparativa de los errores de memoria más antiguos puede ser una sorpresa para algunos, pero hemos descubierto que el código antiguo no es donde necesitamos mejorar con mayor urgencia. Los errores de software package se encuentran y se corrigen con el tiempo, por lo que esperaríamos que la cantidad de errores en el código que se mantiene pero que no se desarrolla activamente disminuya con el tiempo. Así como la reducción del número y la densidad de errores mejora la eficacia del sandboxing, también mejora la eficacia de la detección de errores.

Detección de errores mediante pruebas sólidas, higienización, y borracho es crucial para mejorar la calidad y corrección de todo el program, incluido el computer software escrito en Rust. Una limitación clave para las técnicas de detección de seguridad de la memoria más efectivas es que el estado erróneo debe activarse en el código instrumentado para poder ser detectado. Incluso en bases de código con una excelente cobertura de prueba / fuzz, esto da como resultado que muchos errores pasen desapercibidos.

Otra limitación es que la detección de errores aumenta más rápido que la corrección de errores. En algunos proyectos, los errores que se detectan no siempre se solucionan. La corrección de errores es un proceso largo y costoso.

Cada uno de estos pasos es costoso y omitir alguno de ellos puede hacer que el mistake no se corrija para algunos o todos los usuarios. Para bases de código complejas de C / C ++, a menudo solo hay un puñado de personas capaces de desarrollar y revisar la solución, e incluso con una gran cantidad de esfuerzo dedicado a corregir errores, a veces las correcciones son incorrectas.

La detección de errores es más eficaz cuando los errores son relativamente raros y se les puede dar a los peligrosos la urgencia y prioridad que merecen. Nuestra capacidad para aprovechar los beneficios de las mejoras en la detección de errores requiere que prioricemos la prevención de la introducción de nuevos errores.

Rust moderniza una variedad de otros aspectos del lenguaje, lo que da como resultado una mejora en la corrección del código:

  • Seguridad de la memoria – refuerza la seguridad de la memoria mediante una combinación de comprobaciones de compilador y tiempo de ejecución.
  • Simultaneidad de datos – evita las carreras de datos. La facilidad con la que esto permite a los usuarios escribir código eficiente y seguro para subprocesos ha dado lugar a la Simultaneidad intrépida eslogan.
  • Sistema de tipografía más expresiva – ayuda a prevenir errores de programación lógica (por ejemplo, envoltorios de tipo nuevo, variantes de enumeración con contenido).
  • Las referencias y las variables son inmutables de forma predeterminada: Ayudar al desarrollador a seguir el principio de seguridad de privilegio mínimo, marcando una referencia o variable mutable solo cuando realmente lo pretenda. Si bien C ++ tiene const, tiende a usarse con poca frecuencia y de manera inconsistente. En comparación, el compilador de Rust ayuda a evitar las anotaciones de mutabilidad perdidas al ofrecer advertencias para valores mutables que nunca se mutan.
  • Mejor manejo de errores en bibliotecas estándar – envuelve las llamadas potencialmente fallidas en Final result, lo que hace que el compilador requiera que los usuarios comprueben fallas incluso para funciones que no devuelven un valor necesario. Esto protege contra errores como el Rabia contra la jaula vulnerabilidad que resultó de un mistake no controlado. Facilitando la propagación de errores a través de? Operador y optimizando el Resultado para gastos generales bajos, Rust anima a los usuarios a escribir sus funciones falibles en el mismo estilo y recibir la misma protección.
  • Inicialización – requiere que todas las variables se inicialicen antes de su uso. Históricamente, las vulnerabilidades de la memoria no inicializada han sido la causa principal del 3-5% de las vulnerabilidades de seguridad en Android. En Android 11, comenzamos inicialización automática de memoria en C / C ++ para reducir este problema. Sin embargo, inicializar a cero no siempre es seguro, particularmente para cosas como valores de retorno, donde esto podría convertirse en una nueva fuente de manejo de errores defectuoso. Rust requiere que cada variable se inicialice a un miembro authorized de su tipo antes de su uso, evitando el problema de inicializar involuntariamente a un valor inseguro. Al igual que en Clang para C / C ++, el compilador de Rust es consciente del requisito de inicialización y evita cualquier posible sobrecarga de rendimiento de la doble inicialización.
  • Manejo de enteros más seguro – La desinfección de desbordamiento está activada para las compilaciones de depuración de Rust de forma predeterminada, lo que anima a los programadores a especificar un wrap_insert si realmente quieren que un cálculo se desborde o saturating_include si no lo hacen. Tenemos la intención de habilitar la desinfección de desbordamiento para todas las compilaciones en Android. Además, todas las conversiones de tipos enteros son conversiones explícitas: los desarrolladores no pueden lanzar accidentalmente durante una llamada de función al asignar una variable o al intentar hacer aritmética con otros tipos.

Agregar un nuevo idioma a la plataforma Android es una gran empresa. Hay cadenas de herramientas y dependencias que deben mantenerse, infraestructura de prueba y herramientas que deben actualizarse y desarrolladores que deben recibir capacitación. Durante los últimos 18 meses, hemos estado agregando soporte de Rust al Proyecto de código abierto de Android, y tenemos algunos proyectos de adopción temprana que compartiremos en los próximos meses. Escalar esto a más sistemas operativos es un proyecto de varios años. Estén atentos, publicaremos más actualizaciones en este weblog.

Java es una marca comercial registrada de Oracle y / o sus afiliadas.



Enlace a la noticia authentic