Komfortabler Dateimanager mit vielen Funktionen

Falsch multipliziert

By Sven on 12.08.2005 - 22:57 in C++/ATL

Beim Debuggen von SpeedCommander unter x64 bin ich auf einen unschönen Bug in der ATL vom Visual Studio 2005 gestoßen. Während des Einlesens der Symbolleisten-Einstellungen wurde eine Exception geworfen und SpeedCommander stürzte ab. Nach dem Durchkämpfen bis zum Auslöser der Exception war der Fehler auch recht schnell sichtbar.

Die Exception wurde nach der Funktion AtlMultiplyThrow ausgelöst. AtlMultiplyThrow wird beim Reservieren eines Speicherbereichs aufgerufen und dient dazu, die Größe des zu reservierenden Speicherbereichs aus einer Anzahl von Elementen und deren Blockgröße zu ermitteln. Der Parameter tLeft gibt in diesem Fall die Anzahl der zu reservierenden Elemente und der Parameter tRight die Blockgröße an:

template <typename T>
inline T AtlMultiplyThrow(T tLeft, T tRight)
{
    T tResult;
    HRESULT hr=AtlMultiply(&tResult, tLeft, tRight);
    if(FAILED(hr))
    {
        AtlThrow(hr);
    }
    return tResult;
}

AtlMultiplyThrow ruft lediglich AtlMultiply auf, im Fehlerfall wird eine Exception ausgelöst:

/* generic but compariatively slow version */
template<typename T>
inline HRESULT AtlMultiply(T* ptResult,	T tLeft, T tRight)
{
    /* avoid divide 0 */
    if(tLeft==0)
    {
        return 0;
    }
    if(::ATL::AtlLimits<T>::_Max/tLeft < tRight)
    {
        return E_INVALIDARG;
    }
    *ptResult= tLeft * tRight;
    return S_OK;
}

Der Fehler tritt nur auf, wenn der Wert tLeft Null ist. In diesem Fall gibt AtlMultiply 0 zurück, was auch der Wert für S_OK ist. AtlMultiplyThrow wird erfolgreich verlassen und gibt den Wert von tResult zurück. Wenn man sich die beiden Funktionen nun etwas genauer anschaut, dann entdeckt man, dass der Inhalt von tResult undefiniert ist. In der Debugversion werden alle Werte automatisch mit 0xCCCCCCCC initialisiert, somit gibt AtlMultiplyThrow hier 3435973836 zurück.

Die Allokation des Speichers erfolgt beim Einlesen der Einstellungen, genauer gesagt wenn ein String geladen wird. In der entsprechenden MFC-Funktion wird Speicher entsprechend der Zeichen im String reserviert. Für einen leeren String werden 0 Bytes reserviert, durch den Bug in der Berechnung soll Windows allerdings knapp 3.5 GB bereitstellen. Das kann natürlich nicht gut gehen.

Die Korrektur ist sehr einfach, in der Funktion AtlMultiply muss lediglich die Variable ptResult genullt werden, wenn tLeft den Wert 0 enthält:

/* generic but compariatively slow version */
template<typename T>
inline HRESULT AtlMultiply(T* ptResult,	T tLeft, T tRight)
{
    /* avoid divide 0 */
    if(tLeft==0)
    {
        *ptResult = 0;
        return S_OK;
    }
    if(::ATL::AtlLimits<T>::_Max/tLeft < tRight)
    {
        return E_INVALIDARG;
    }
    *ptResult= tLeft * tRight;
    return S_OK;
}

Abschließend muss die MFC zwingend neu kompiliert werden, da der Fix ansonsten nur für eigene Anwendungen gilt und nicht für Funktionen innerhalb der MFC.

Es können keine Kommentare abgegeben werden.

Top