Back to Home

Consequences of rewriting Firefox components in Rust

Firefox · Rust · Quantum CSS · refactoring · memory security

Consequences of rewriting Firefox components in Rust

Original author: Diane Hosfelt
  • Transfer
In previous articles in the series, we discussed memory security and thread safety in Rust. In this last article, we’ll look at the implications of a real Rust application using the Quantum CSS project as an example .

The CSS engine applies CSS rules to the page. This is a descending process that descends the DOM tree, after calculating the parent CSS, the child styles can be calculated independently: ideal for parallel computing. By 2017, Mozilla made two attempts to parallelize the style system using C ++. Both failed.

Quantum CSS development has begun to increase productivity. Improving security is just a good side effect.


There is a certain connection between memory protection and information security bugs. Therefore, we expected that using Rust would reduce the attack surface in Firefox. This article will look at potential vulnerabilities that have been identified in the CSS engine since the initial release of Firefox in 2002. Then look at what could and could not have been prevented with Rust.

For all time, 69 security errors have been detected in the CSS component of Firefox. If we had a time machine and we could write it Rust from the very beginning, then 51 (73.9%) errors would become impossible. Although Rust makes writing good code easier, it also offers no absolute protection.

Rust


Rust is a modern system programming language that is safe for types and memory. As a side effect of these security guarantees, Rust programs are also thread safe at compile time. Thus, Rust is particularly suitable for:

  • secure processing of unreliable incoming data;
  • concurrency to improve performance;
  • integration of individual components into the existing code base.

However, Rust does not explicitly fix some error classes, especially correctness errors. In fact, when our engineers rewrote Quantum CSS, they accidentally repeated a critical security bug, which was previously fixed in C ++ code, they accidentally deleted the bug fix 641731 , which allows leakage of global history via SVG. The error was re-registered as bug 1420001 . A history leak is rated as a critical security vulnerability. The initial fix was an additional check to see if the SVG document is an image. Unfortunately, this check was missed when rewriting the code.

Although automated tests should find rule violations:visitedlike this, in practice they did not find this error. To speed up automatic tests, we temporarily disabled the mechanism that tested this feature - tests are not particularly useful if they are not performed. The risk of re-implementing logical errors can be reduced by good test coverage. But there is still the danger of new logical errors.

As a developer becomes familiar with Rust, his code becomes even more secure. Although Rust does not prevent all possible vulnerabilities, it fixes a whole class of the most serious bugs.

Quantum CSS Security Errors


In general, by default, Rust prevents errors related to memory, boundaries, null / uninitialized variables, and integer overflows. The non-standard bug mentioned above remains possible: a crash occurs due to failed memory allocation.

Security Errors by Category


  • Memory: 32
  • Borders: 12
  • Implementation: 12
  • Null: 7
  • Stack Overflow: 3
  • Integer overflow: 2
  • Other: 1
In our analysis, all bugs are related to security, but only 43 received an official rating (it is assigned by Mozilla's security engineers based on qualified assumptions about "exploitability"). Ordinary bugs may indicate missing functions or some kind of malfunction, which does not necessarily lead to data leakage or behavior change. Official security errors range from low importance (if there is a strong restriction on the attack surface) to critical vulnerability (may allow an attacker to run arbitrary code on the user's platform).

Memory vulnerabilities are often classified as serious security issues. Of the 34 critical / serious issues, 32 were related to memory.

Severity distribution of security bugs


  • Total: 70
  • Security Errors: 43
  • Critical / Serious: 34
  • Fixed Rust: 32

Comparing Rust and C ++


Bug 955913  - heap buffer overflow in function GetCustomPropertyNameAt. The code used the wrong variable for indexing, which led to the interpretation of memory after the end of the array. This may cause a crash when accessing a bad pointer or copying memory to a string that is passed to another component.

The order of all CSS properties (including custom, i.e. custom) is stored in an array mOrder. Each element is represented either by a CSS property value, or, in the case of custom properties, a value that begins with eCSSProperty_COUNT(total number of non-custom CSS properties). To get the name of custom properties, you first need to get the value from mOrder, and then access the name in the corresponding array indexmVariableOrder, which stores the names of custom properties in order.

Vulnerable C ++ Code:


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

The problem occurs on line 6 when used aIndexto access an array element mVariableOrder. The thing is that it aIndexshould be used with an array mOrder, not mVariableOrder. The corresponding element for the custom property represented aIndexin is mOrder, in fact mOrder[aIndex] - eCSSProperty_COUNT.

Corrected C ++ Code:


    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]);
    }

Corresponding Rust code


Although Rust is somewhat similar to C ++, it uses other abstractions and data structures. Rust code will be very different from C ++ (see below for more details). First, let's look at what happens if the vulnerable code is translated as literally as possible:

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

The Rust compiler will accept this code because the length of the vectors cannot be determined before execution. Unlike arrays, the length of which should be known, the type Vec in Rust has a dynamic size. However, bounds checking is built into the implementation of the standard library vector. If an invalid index appears, the program immediately terminates in a controlled manner, preventing any unauthorized access.

Real codeQuantum CSS uses very different data structures, so there is no exact equivalent. For example, we use Rust's powerful built-in data structures to unify the arrangement and property names. This eliminates the need to maintain two independent arrays. Rust data structures also improve data encapsulation and reduce the likelihood of such logical errors. Since the code should interact with C ++ code in other parts of the browser, the new function GetCustomPropertyNameAtdoes not look like Rust's idiomatic code. But it still gives all the guarantees of security, while providing a more understandable abstraction of the underlying data.

tl; dr


Because vulnerabilities are often associated with memory security breaches, Rust code should significantly reduce the number of critical CVEs . But even Rust is not perfect. Developers still need to track for correctness errors and data leak attacks. Support for secure libraries still requires code reviews, tests, and fuzzing.

Compilers cannot catch all programmer errors. Nevertheless, Rust takes off our memory security burden, allowing us to focus on the logical correctness of the code.

Read Next