Consecuencias de reescribir los componentes de Firefox en Rust

En art√≠culos anteriores de la serie, discutimos la seguridad de la memoria y la seguridad de los hilos en Rust. En este √ļltimo art√≠culo, veremos las implicaciones de una aplicaci√≥n Rust real usando el proyecto Quantum CSS .

El motor CSS aplica reglas CSS a la página. Este es un proceso descendente que desciende el árbol DOM, después de calcular el CSS principal, los estilos secundarios se pueden calcular de forma independiente: ideal para la computación paralela. Para 2017, Mozilla hizo dos intentos de paralelizar el sistema de estilo usando C ++. Ambos fallaron.

El desarrollo cu√°ntico de CSS ha comenzado a aumentar la productividad. Mejorar la seguridad es solo un buen efecto secundario.


Existe una cierta conexión entre la protección de la memoria y los errores de seguridad de la información. Por lo tanto, esperábamos que el uso de Rust redujera la superficie de ataque en Firefox. Este artículo analizará las vulnerabilidades potenciales que se han identificado en el motor CSS desde el lanzamiento inicial de Firefox en 2002. Luego mira lo que podría y no pudo haberse evitado con Rust.

Para siempre, se han detectado 69 errores de seguridad en el componente CSS de Firefox. Si tuviéramos una máquina del tiempo y pudiéramos escribir Rust desde el principio, entonces 51 (73.9%) errores serían imposibles. Aunque Rust facilita la escritura de un buen código, tampoco ofrece protección absoluta.

Herrumbre


Rust es un lenguaje de programación de sistema moderno que es seguro para los tipos y la memoria. Como efecto secundario de estas garantías de seguridad, los programas Rust también son seguros para subprocesos en tiempo de compilación. Por lo tanto, Rust es particularmente adecuado para:

  • procesamiento seguro de datos entrantes poco confiables;
  • concurrencia para mejorar el rendimiento;
  • integraci√≥n de componentes individuales en la base de c√≥digo existente.

Sin embargo, Rust no corrige explícitamente algunas clases de error, especialmente los errores de corrección. De hecho, cuando nuestros ingenieros reescribieron Quantum CSS, repitieron accidentalmente un error de seguridad crítico, que anteriormente se solucionó en código C ++, eliminaron accidentalmente la corrección de errores 641731 , que permite la filtración de la historia global a través de SVG. El error se volvió a registrar como error 1420001 . Una fuga en el historial se considera una vulnerabilidad de seguridad crítica. La solución inicial fue una verificación adicional para ver si el documento SVG es una imagen. Desafortunadamente, esta verificación se perdió al reescribir el código.

Aunque las pruebas automatizadas deber√≠an encontrar violaciones de la regla :visited esta manera, en la pr√°ctica no encontraron este error. Para acelerar las pruebas autom√°ticas, deshabilitamos temporalmente el mecanismo que prob√≥ esta caracter√≠stica; las pruebas no son particularmente √ļtiles si no se realizan. El riesgo de volver a implementar errores l√≥gicos se puede reducir con una buena cobertura de prueba. Pero todav√≠a existe el peligro de nuevos errores l√≥gicos.

A medida que un desarrollador se familiariza con Rust, su c√≥digo se vuelve a√ļn m√°s seguro. Aunque Rust no previene todas las vulnerabilidades posibles, corrige toda una clase de los errores m√°s graves.

Errores de seguridad CSS cu√°nticos


En general, de forma predeterminada, Rust evita errores relacionados con la memoria, límites, variables nulas / no inicializadas y desbordamientos de enteros. El error no estándar mencionado anteriormente sigue siendo posible: se produce un bloqueo debido a una asignación de memoria fallida.

Errores de seguridad por categoría


  • Memoria: 32
  • Fronteras: 12
  • Implementaci√≥n: 12
  • Nulo: 7
  • Desbordamiento de pila: 3
  • Desbordamiento de enteros: 2
  • Otro: 1
En nuestro an√°lisis, todos los errores est√°n relacionados con la seguridad, pero solo 43 recibieron una calificaci√≥n oficial (es asignada por los ingenieros de seguridad de Mozilla con base en suposiciones calificadas sobre "explotabilidad"). Los errores comunes pueden indicar funciones faltantes o alg√ļn tipo de mal funcionamiento, lo que no necesariamente conduce a la fuga de datos o al cambio de comportamiento. Los errores de seguridad oficiales var√≠an desde baja importancia (si hay una fuerte restricci√≥n en la superficie de ataque) hasta vulnerabilidad cr√≠tica (puede permitir que un atacante ejecute c√≥digo arbitrario en la plataforma del usuario).

Las vulnerabilidades de memoria a menudo se clasifican como problemas de seguridad graves. De los 34 problemas críticos / graves, 32 estaban relacionados con la memoria.

Distribución de gravedad de errores de seguridad.


  • Total: 70
  • Errores de seguridad: 43
  • Cr√≠tico / grave: 34
  • √ďxido fijo: 32

Comparación de óxido y C ++


Error 955913 : desbordamiento del b√ļfer de almacenamiento din√°mico en la funci√≥n GetCustomPropertyNameAt . El c√≥digo utiliz√≥ la variable incorrecta para la indexaci√≥n, lo que condujo a la interpretaci√≥n de la memoria despu√©s del final de la matriz. Esto puede provocar un bloqueo al acceder a un puntero incorrecto o al copiar memoria en una cadena que se pasa a otro componente.

El orden de todas las propiedades CSS (incluidas las personalizadas, es decir, personalizadas) se almacena en la matriz mOrder . Cada elemento est√° representado por un valor de propiedad CSS o, en el caso de propiedades personalizadas, un valor que comienza con eCSSProperty_COUNT (n√ļmero total de propiedades CSS no personalizadas). Para obtener el nombre de las propiedades personalizadas, primero debe obtener el valor de mOrder y luego acceder al nombre en el √≠ndice correspondiente de la matriz mVariableOrder , que almacena los nombres de las propiedades personalizadas en orden.

Código vulnerable de C ++:


  void GetCustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const { MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT); aResult.Truncate(); aResult.AppendLiteral("var-"); aResult.Append(mVariableOrder[aIndex]); 

El problema ocurre en la línea 6 cuando se usa aIndex para acceder al elemento de la matriz mVariableOrder . El hecho es que aIndex debe usarse con la matriz mOrder , no con la mVariableOrder . El elemento correspondiente para la propiedad personalizada representada por aIndex en mOrder es en realidad mOrder[aIndex] - eCSSProperty_COUNT .

Código C ++ corregido:


  void Get CustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const { MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT); uint32_t variableIndex = mOrder[aIndex] - eCSSProperty_COUNT; aResult.Truncate(); aResult.AppendLiteral("var-"); aResult.Append(mVariableOrder[variableIndex]); } 

Código de óxido correspondiente


Aunque Rust es algo similar a C ++, utiliza otras abstracciones y estructuras de datos. El código de óxido será muy diferente de C ++ (ver más abajo para más detalles). Primero, veamos qué sucede si el código vulnerable se traduce lo más literalmente posible:

  fn GetCustomPropertyNameAt(&self, aIndex: usize) -> String { assert!(self.mOrder[aIndex] >= self.eCSSProperty_COUNT); let mut result = "var-".to_string(); result += &self.mVariableOrder[aIndex]; result } 

El compilador de Rust aceptar√° este c√≥digo porque la longitud de los vectores no se puede determinar antes de la ejecuci√≥n. A diferencia de las matrices, cuya longitud debe conocerse, el tipo Vec en Rust tiene un tama√Īo din√°mico. Sin embargo, la verificaci√≥n de l√≠mites est√° integrada en la implementaci√≥n del vector de biblioteca est√°ndar. Si aparece un √≠ndice no v√°lido, el programa termina inmediatamente de manera controlada, evitando cualquier acceso no autorizado.

El c√≥digo real de Quantum CSS utiliza estructuras de datos muy diferentes, por lo que no hay un equivalente exacto. Por ejemplo, utilizamos las poderosas estructuras de datos integradas de Rust para unificar la disposici√≥n y los nombres de propiedad. Esto elimina la necesidad de mantener dos matrices independientes. Las estructuras de datos de √≥xido tambi√©n mejoran la encapsulaci√≥n de datos y reducen la probabilidad de tales errores l√≥gicos. Dado que el c√≥digo debe interactuar con el c√≥digo C ++ en otras partes del navegador, la nueva funci√≥n GetCustomPropertyNameAt se parece al c√≥digo idiom√°tico de Rust. Pero a√ļn ofrece todas las garant√≠as de seguridad, al tiempo que proporciona una abstracci√≥n m√°s comprensible de los datos subyacentes.

tl; dr


Debido a que las vulnerabilidades a menudo se asocian con infracciones de seguridad de la memoria, el c√≥digo Rust deber√≠a reducir significativamente la cantidad de CVE cr√≠ticos. Pero incluso Rust no es perfecto. Los desarrolladores a√ļn necesitan rastrear los errores de correcci√≥n y los ataques de fuga de datos. El soporte para bibliotecas seguras a√ļn requiere revisiones de c√≥digo, pruebas y fuzzing.

Los compiladores no pueden detectar todos los errores de programador. Sin embargo, Rust nos quita la carga de seguridad de la memoria, lo que nos permite centrarnos en la corrección lógica del código.

Source: https://habr.com/ru/post/445670/


All Articles