Filter: Bug

Bug: Verbund mit Fließkommazahlen

Ein recht interessanter, nicht direkt offensichtlicher Fehler, kam mir vor einiger Zeit unter die Finger. Hierzu ein Stück Code:

class CMyVariant
{
public:
    CMyVariant();
    virtual ~CMyVariant();
 
    // Getter & Setter
 
protected:
    union
    {
        int64_t nTime;
        float fValue;
    };
};
 
int main()
{
    CMyVariant v1;
    ...
    // Variant v1 setzt nTime
    ... 
    CMyVariant v2 = v1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Auswirkungen

Die Klasse soll einen Variant-Datentypen darstellen, der zum Transport von Werten über Netzwerke und innerhalb von Anwendung benutzt werden kann. Der Wert nTime hält eine Zeit in Millisekunden, dieser sollte die aktuelle Uhrzeit tragen, war jedoch ca. alle 24 Tage um 70 Minuten versetzt. Wie dass?

Analyse

Da es eine Regelmäßigkeit gibt, die scheinbar mit der Zeit zusammenhängt, lassen sich die Werte gut zurückrechnen (in Millisekunden):

  • 70 Minuten = 4200000 Millisekunden = 0x00401640
  • 24 Tage = 2073600000 Millisekunden…

Bug: Timeouts in einer Multithreadapplikation

Vor kurzem lief mir ein interessanter Bug über den Weg, dazu erst kurzer Beispielcode:

uint64_t nTimeout;
 
void Func1()
{
    uint64_t nTickCount = GetTickCount();
    myLock.lock();
    ...
    nTimeout = nTickCount;
    ...
    myLock.unlock();
}
 
void Func2()
{
    uint64_t nTickCount = GetTickCount();
    myLock.lock();
    ...
    if((nTickCount-nTimeout)/1000>30) // Beispiel 30s 
        // Timeout
    ...
    myLock.unlock();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Auswirkungen

Func1 und Func2 werden von unterschiedlichen Threads aufgerufen. Func1 setzt ab und zu, aber rechtzeitig innerhalb der 30s den Timeout zurück. Trotzdem wirft Func2 ab und zu einen Timeout.

Analyse

Der Code hat mindestens zwei Probleme:

Das erste Problem ist, dass der TickCounter jeweils vor der Sperre gelesen wird. So kann es passieren, das innerhalb der Sperre in Func2 nTickCount kleiner als nTimeout ist. Dies ist dann der Fall, wenn nach dem Lesen des TickCounters in Func2 der Thread angehalten wird…