Seltsame Abstürze in DrawItem(..)



  • Hallo zusammen,

    ich komme hier mit immer seltsameren Fragen.
    Ich entschuldige mich schon mal im Voraus dafür.

    Aber ich habs nicht geschrieben! Ich bin unschuldig!

    Ein 20 Jahre altes Programm stürzt in "DrawItem(..)" gelegentlich ab.
    Es wurde in "Visual c++ 6.0" geschrieben.
    Die DrawItem-Fktn. ist allerding selbstgeschrieben (nicht von mir) und ersetzt
    die Windows Version davon.
    Der Aufruf erfolgt aber durch Messaging, irgendwie.

    Aktuell habe ich das Programm auf Visual Studio 2017 portiert, da bei den
    neueren VS dies viel häufiger passiert.
    Ausserdem passiert es nur beim Kunden! Nur dort habe ich ein entsprechendes Setup der Anwendung. (Da versuch ich nun gerade mein Glück)
    Auch weiss ich nicht zu 100%, ob die Absturzursache beider Versionen auch wirklich dieselbe ist.

    Unter VS 2017 (und auch unter VS 2005) passiert folgendes:

    void CMehrLB::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
      int iIndex=0, iHilfIndex=0, i=0;	// HP, 21.02.18: Vars auf "0" initialisiert
      char szOutput[120];
      LPSTR lpstrOut;
      CBitmap *pCBild=NULL, *pCBildMask=NULL, *pCKreuz=NULL, *pCKreuzMask=NULL;
      CFont *pFontOld;
      CDC *pCDC=CDC::FromHandle(lpDrawItemStruct->hDC);
      CBrush *pCBrush;
      CSize size;
      RECT struRect, struRectText;
      int iScrollPos;
    
      // richtigen Font für diesen Context setzen - h.kolb 97,08,20
      if (GetFont()!=NULL) pFontOld = pCDC->SelectObject(GetFont());
      else pFontOld = NULL;
      // die aktuelle Scrollposition holen
      iScrollPos = GetScrollPos(SB_HORZ);
      // Normales Paint für leere Listbox
      if (lpDrawItemStruct->itemID==-1) // HP, 20.02.18: Welcher Idiot prüft einen UINT auf "-1" ab ??? Aber man kann auch nichts ergänzen.
      {
        pCDC->DrawFocusRect(&(lpDrawItemStruct->rcItem));
        if (pFontOld!=NULL) pCDC->SelectObject(pFontOld);
        return;
      }
    
      // Daten zum aktuellen Index holen
      iIndex = lpDrawItemStruct->itemData;	// HP, 20.02.18: Im Absturz-Fall ist iIndex auf einem extxrem grossen Wert.
    									    // Eine zus. Abfrage auf diesen Wert half aber bisher nichts. Ich versuchs nachfolgend trotzdem nochmal (21.02.18).
      if (iIndex > 500) {					// HP, 21.02.18: Das ist der Versuch. --> wirkt aber nicht! Mann müsste die Grösse de "m_pRecord" ermitteln.
    	  TRACE("1. iIndex = %d\n", iIndex);
    	  return;
      }
    
      // Auf die LB-Struktur zugreifen
      if (m_hgmRecord!=NULL)
      {
    

    Irgendwo sehr weit unten kommt dann folgender Aufruf:

    // Text ausgeben
    		if ((iIndex > 500) || (iIndex < 0)) {					// HP, 21.02.18: Das ist zusätzl. Versuch. --> wirkt aber nicht!
    			TRACE("2. iIndex = %d\n", iIndex);
    			return;
    		}
    		strcpy(szOutput, m_pRecord[iIndex].szText);// HP, 20.02.18: hier passiert immer der Absturz, mit iIndex = sehr grosser Wert!
    

    Eigentlich steht das Problem in meinen Kommentaren.
    Aber hier nochmal
    Der Wert von "iInex" hat plötzlich irgendeine astronomische Grösse (nicht immer dieselbe!).
    Also etwa 1784755478.
    Aber ich kann den Wert (wie im Code meine Versuche zeigen) nicht abfangen.
    Es scheint wie so ein "selfmodifiing" Zeug zu seiin.

    Weiss da jeand vielleicht einen Rat?

    Grüsse
    Helmut



  • Und zur Ergänzung ...
    Bevor es zur Absturzstelle kommt und dann abstürzt, wurde auf "iIndex" schon ein paarmal zugegriffen.
    Es wird aber im Verlauf dieser Methode nirgends mehr neu gesetzt, nur noch gelesen.

    Es muss wohl von einer anderen Stelle, ausserhalb beeinflusst werden.
    Und während die mit VC++ 6.0 erzeugte exe keine Probleme macht, stürzt die mit VS 2005 oder VS 2017 erzeugte exe irgendwann ab.

    Und manchmal eben, nur wesentlich seltener, stürzt auch die alte exe ab.

    Man kann nur feststellen, dass es mit bestimmten externen Ereignissen zu tun hat,
    nur nicht genau welche.

    Das Programm liest über RPC Daten ein.
    Aus diesen Daten wird ein String zusammengestellt, der in einer ListBox zur Anzeige gebracht werden soll.
    Zyklisch wird geprüft, ob sich die Werte, die in den String geschrieben werden, verändert haben.
    Das neu Schreiben der Listbox wird dann sicherlich über ein "Invalidate()" ausgelöst.

    Aber sollte nicht an anderer Stelle der Fehler auftreten, wo die eigentliche Speicherverletzung stattfindet?

    Grüsse
    Helmut



  • Hallo zusammen,

    habe den einen Fehler nun doch gefunden.
    Aber so isses halt, wenn man nur 3 Tage Zeit hat. Der Stress zwingt einen dann doch noch ins Forum um Hilfe zu posten.

    Der Ziel-String für das "strcpy(..)" war kürzer als der Quell-String definiert.
    Und das nun schon seit 20 Jahren. Und kein warning im Compiler. und dieser veränderte "iIndex" dazu.

    Alter und die neuen Compiler haben tatsächlich unterschiedliche Ergebnisse geliefert.
    Beim alten Compiler blieb die Ausgabe einfach nur leer, während das Produkt der neuen Compiler abstürzte.

    Jetzt sollte ich nur noch wissen, warum auch manchmal die alten Binaries abstürzten (oder vielleicht immernoch abstürzen)

    Danke an die, die es gelesen haben und Entschuldigung für die Belästigung.

    Grüsse
    Helmut



  • Das ist "undefiniertes Verhalten", daher kann alles passieren (also auch ein Absturz).

    Technisch gesehen werden beim Überschreiten des lokalen Arrays (szOutput) die lokalen Variablen, welche vorher deklariert sind (da der Stack von oben nach unten wächst), überschrieben (bei dir also iIndex, iHilfIndex, i). Und wenn noch mehr kopiert (d.h. überschrieben) wird, dann werden auch die Rücksprungadressen der Funktionen überschrieben (welches bei zufälligen Werten dann meistens zu einem "Illegal Address Interrupt" führt => crash).

    Und dann kommt es auch noch darauf an, mit welchen Optimierungsoptionen kompiliert wurde (Debug, Release)...

    PS: Eine Compiler-Warnung wirst du nicht erwarten dürfen, da ja erst zur Laufzeit bei strcpy feststeht, wieviel Speicher kopiert wird.


  • Mod

    Mit strcpy_s würde das nicht passieren!

    Wenn Du solche Crashes in einem Bereich hast, dann gönn es Dir und überarbeite alle unsicheren CRT Funktionen!



  • Erledigt



  • In einem anderen Projekt habe auch schon diese stri g Funktionen gegen diese "secure" Funktionen ausgetauscht.
    Es war ja hier gar nicht geplant, das weiter zu entwickeln.
    Und dann hängt man in seiner eigenen Gedankenschleife fest
    Und das frustrierende ist. Obwohl man den Fehler gefunden hat,
    denkt man: Das hätte mir auch früher auffallen können.
    Na ja...


Log in to reply