Seltsames Verhalten von C # im Debugger gegenüber der normalen Ausführung hat einen Heisenbug verursacht

9

Ich habe gerade die letzte Stunde damit verbracht, ein bizarres Problem mit nicht verwaltetem Speicher in C # anzugehen.

Zuerst ein bisschen Kontext. Ich habe eine C # -DLL, die einige native Methoden (via diese tolle Projektvorlage ) exportiert werden dann von einer Delphi-Anwendung aufgerufen. Eine dieser C # -Methoden muss eine Struktur an Delphi übergeben, wo sie in einen Datensatz umgewandelt wird. Ich kann schon sagen, dass Ihnen übel ist, deshalb werde ich nicht näher darauf eingehen. Ja, es ist hässlich, aber die Alternative ist COM ... Nein danke.

Hier ist eine Vereinfachung des fehlerhaften Codes:

%Vor%

In Wirklichkeit gibt es hier noch ein paar andere Dinge, die mit dem Tracking nativer Ressourcen zu tun haben, aber das ist es im Grunde genommen. Wenn Sie den Fehler bereits bemerkt haben, gut gemacht.

Im Wesentlichen bestand das Problem darin, dass ich WriteInt16 anstelle von WriteByte wegen eines IntelliSense-gestützten Tippfehlers verwendete, was dazu führte, dass die letzte Iteration ein Byte über das Ende des Puffers geschrieben wurde. Einfacher Fehler zu machen, denke ich.

Was das Debuggen jedoch so schwer machte, war, dass es im Debugger im Hintergrund fehlschlug und der Rest der Anwendung weiter funktionierte. Der Speicher war belegt, und bis auf das letzte Byte war alles null, also funktionierte es. Wenn es außerhalb eines Debuggers gestartet wurde, stürzte die Anwendung mit einer Zugriffsverletzung ab. Eine klassische Heisenbug-Situation - der Fehler verschwindet, wenn Sie versuchen, ihn zu analysieren. Beachten Sie, dass dieser Absturz nicht eine verwaltete Ausnahme war, sondern eine echte Zugriffsverletzung auf CPU-Ebene.

Nun, das verwirrt mich aus zwei Gründen:

  1. Die Ausnahme wurde NICHT ausgelöst, wenn ein Debugger angefügt wurde - ich habe Visual Studio, CodeGear Delphi 2009 und OllyDbg ausprobiert. Wenn angebracht, funktionierte das Programm perfekt. Wenn es nicht angehängt wurde, stürzte das Programm ab. Ich habe für alle Versuche dieselbe ausführbare Datei verwendet. Mein Verständnis ist, dass das Debugging das Verhalten der Anwendung nicht ändern sollte, aber es tat es eindeutig.

  2. Normalerweise würde ich erwarten, dass dieser Vorgang einen AccessViolationException innerhalb meines verwalteten Codes verursacht, aber stattdessen mit einer Speicherzugriffsverletzung in ntdll.dll verstarb.

Nun, fairerweise ist mein Fall wahrscheinlich einer der undurchsichtigsten (und möglicherweise fehlgeleiteten) Eckfälle in der Geschichte von C #, aber ich bin verloren, wie das Anhängen eines Debuggers den Absturz verhinderte. Ich bin besonders überrascht, dass es unter OllyDbg funktioniert hat, was den Prozess nicht annähernd so behindert wie Visual Studio.

Also, was zum Teufel ist hier passiert? Warum wurde die Ausnahme während des Debuggen verschluckt (oder nicht ausgelöst), aber nicht außerhalb des Debuggers? Und warum wurde keine Ausnahme für verwaltete Zugriffsverletzung ausgelöst, als ich versuchte, Marshal.WriteInt16 außerhalb des zugewiesenen Speicherblocks aufzurufen, wie es in der Dokumentation heißt?

    
Polynomial 03.10.2012, 10:34
quelle

1 Antwort

2

Sie haben nur einige Heapspeicher auf Null gesetzt. Das scheitert nicht, da es keine Checks gibt, von denen Sie diese Adresse erhalten haben. Dies erzeugt später ein Problem in der Heap-Verwaltung (in ntdll). Diese Seite zeigt, warum nur ein Überlauf erfolgt später erkannt.

Sie scheitern nicht mit angehängtem Debugger aus ähnlichen Gründen. Wenn Windows erkennt, dass ein Debugger angeschlossen ist, wird ein anderer Heap-Manager verwendet. Der Debug-Heap-Manager fügt ein Suffix zum Erkennen von Überläufen hinzu, das den Heap-Manager nicht deaktiviert, aber die Beschädigung beim Freigeben des Blocks anzeigt. Weitere Details finden Sie auf der obigen Seite.

Wie der Heap-Manager auf os-Ebene geschaltet wird, ist mir nicht bekannt, aber ich fand eine ähnliche Antwort auf stackoverflow: Visual C ++: Unterschied zwischen Start mit / ohne Debugging im Release-Modus

Wenn ich den Wechsel des Heap-Managers verstehe, sollte der Debugger bei der Verwendung des Standard-Heap-Managers auf der Heap-Beschädigung anhalten, wenn Sie den Prozess im Freigabemodus über den Desktop / die Konsole starten und später einen Debugger anfügen. Dies könnte als Test für meine Annahmen verwendet werden.

    
sanosdole 03.10.2012 17:18
quelle