xorl %eax, %eax

CVE-2009-3380: Mozilla Firefox Invalid Pointer Access

leave a comment »

This bug was announced in MFSA 2009-64 and as the advisory mentions, the bug was reported by Vladimir Vukicevic, Jesse Ruderman, Martijn Wargers, Daniel Banchero, David Keeler, and Boris Zbarsky as crash in the browser engine. Here is the buggy code as seen in gfx/src/thebes/nsThebesDeviceContext.cpp C++ source code file.

protected:
    nsTArray<nsIFontMetrics*> mFontMetrics;
    nsIDeviceContext         *mContext; // we do not addref this since
                                        // ownership is implied. MMP.
};
    ...
nsresult nsFontCache::Compact()
{
    // Need to loop backward because the running element can be removed on
    // the way
    for (PRInt32 i = mFontMetrics.Length()-1; i >= 0; --i) {
        nsIFontMetrics* fm = mFontMetrics[i];
        nsIFontMetrics* oldfm = fm;
        // Destroy() isn't here because we want our device context to be
        // notified
        NS_RELEASE(fm); // this will reset fm to nsnull
        // if the font is really gone, it would have called back in
        // FontMetricsDeleted() and would have removed itself
        if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) { 
            // nope, the font is still there, so let's hold onto it too
            NS_ADDREF(oldfm);
        }
    }
    return NS_OK;
}

This is a simple ‘for’ loop that iterates through each ‘mFontMetrics[]’ member and uses NS_RELEASE() macro from xpcom/glue/nsISupportsUtils.h to reset the entry stored in ‘fm’ pointer like this:

/**
 * Macro for releasing a reference to an interface.
 * @param _ptr The interface pointer.
 */
#define NS_RELEASE(_ptr)                                                      \
  PR_BEGIN_MACRO                                                              \
    (_ptr)->Release();                                                        \
    (_ptr) = 0;                                                               \
  PR_END_MACRO

Back to Compact() we can see that if the ‘mFontMetrics.IndexOf(oldfm)’ is not equal to ‘mFontMetrics.NoIndex’ which means that it has some index value and thus, the font was not removed, it will invoke NS_ADDREF() macro on that interface pointer. This pre-processor code resides in xpcom/glue/nsISupportsUtils.h header file:

/**
 * Macro for adding a reference to an interface.
 * @param _ptr The interface pointer.
 */
#define NS_ADDREF(_ptr) \
  (_ptr)->AddRef()

Vladimir Vukicevic (aka vlad) noticed that destructor code for ‘nsThebesFontMetrics’ was the following:

nsThebesFontMetrics::~nsThebesFontMetrics()
{
    delete mFontStyle;
    //delete mFontGroup;
}
       ...
NS_IMETHODIMP
nsThebesFontMetrics::Destroy()
{
    return NS_OK;
}

This means means that the actual destructor will only delete a member. By doing so, the ‘fm’ object will call ‘Destroy()’ which will simply return ‘NS_OK’ without executing anything else, and the subsequent call to NS_RELEASE() will access an invalid pointer.
This was fixed by applying the following patch:

 nsThebesFontMetrics::~nsThebesFontMetrics()
 {
+    if (mDeviceContext)
+        mDeviceContext->FontMetricsDeleted(this);
     delete mFontStyle;
     //delete mFontGroup;
 }
@@ -102,6 +104,7 @@ 
 NS_IMETHODIMP
 nsThebesFontMetrics::Destroy()
 {
+    mDeviceContext = nsnull;
     return NS_OK;
 }
 

This will set the equivalent ‘mDeviceContext’ to NULL when Destroy() method is called. Then, when the destructor is reached it will check that value before executing FontMetricsDeleted() callback function.
In addition to this, a test case was added in gfx/thebes/mochitest/ directory named ‘test_bug509244.html‘ that could be used to trigger the vulnerability.

<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=509244
-->
<head>
  <title>Test for Bug 509244</title>
  <script type="application/javascript" src="/MochiKit/packed.js"></script>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=509244">Mozilla Bug 509244</a>
<p id="display">A</p>
<div id="content" style="display: none">
  
</div>
<pre id="test">
<script type="application/javascript">

/** Test for Bug 509244 **/

function flush() { document.documentElement.offsetHeight; }

var text = document.getElementById("display");

// layout text, caching monospace font
text.style.fontFamily = "monospace";
flush();
// relayout text so that monospace font is no longer used (but cached)
text.style.fontFamily = "sans-serif";
flush();

// flush cache
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var os = Components.classes["@mozilla.org/observer-service;1"]
         .getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, "memory-pressure", "heap-minimize");

// reuse font that was flushed from cache
text.style.fontFamily = "monospace";
flush(); 

ok(true, "not crashed");

</script>
</pre>
</body>
</html>

So basically, this is a simple JavaScript code that will perform various memory flushes as well as cache memory ones to attempt to crash the browser engine.
This vulnerability can be found in both Firefox 3.5 and 3.0 prior to 3.5.4 and 3.0.15 releases respectively. As Mozilla suggested, a temporary solution can be to disable JavaScript support.

Written by xorl

November 2, 2009 at 23:49

Posted in bugs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s