Random stürzt unter Windows 10 64bit mit ATL-Subklassen ab

8

Von Anfang an: Seit dem 1. März 2017 ist dies ein Bug, der von Microsoft bestätigt wurde. Lesen Sie die Kommentare am Ende.

Kurzbeschreibung:

Ich habe zufällige Abstürze in größeren Anwendungen mit MFC, ATL. In all diesen Fällen, nachdem ATL-Subklassen für ein Fenster bei einfachen Aktionen mit einem Fenster verwendet wurden (Verschieben, Größenänderung, Fokus setzen, Malen usw.), bekomme ich einen Absturz bei einer zufälligen Ausführungsadresse.

Zuerst sah es aus wie eine wilde Pointer- oder Heap-Beschädigung, aber ich beschränkte das komplette Szenario auf eine sehr einfache Anwendung, die reines ATL und nur Windows API verwendete.

Anforderungen / meine verwendeten Szenarien:

  • Die Anwendung wurde mit VS 2015 Enterprise Update 3 erstellt.
  • Das Programm sollte als 32bit kompiliert werden.
  • Die Testanwendung verwendet CRT als freigegebene DLL.
  • Die Anwendung läuft unter Windows 10 Build 14393.693 64bit (aber wir haben Repros unter Windows 8.1 und Windows Server 2012 R2, alle 64bit)
  • atlthunk.dll hat die Version 10.0.14393.0

Was die Anwendung macht:

Er erstellt einfach ein Rahmenfenster und versucht, viele statische Fenster mit der Windows-API zu erstellen. Nachdem das statische Fenster erstellt wurde, unterliegt dieses Fenster der ATL CWindowImpl :: SubclassWindow-Methode. Nach der Unterklassenoperation wird eine einfache Fensternachricht gesendet.

Was passiert:

Nicht bei jedem Lauf, aber sehr oft stürzt die Anwendung bei SendMessage auf das Unterklasse-Fenster ab. Auf dem 257-Fenster (oder einem anderen Vielfachen von 256 + 1) schlägt die Unterklasse in irgendeiner Weise fehl. Der ATL-Thunk, der erstellt wird, ist ungültig. Es scheint, dass die gespeicherte Ausführungsadresse der neuen Unterklassenfunktion nicht korrekt ist. Das Senden der Nachricht an das Fenster verursacht einen Absturz. Der Callstack ist immer derselbe. Die letzte sichtbare und bekannte Adresse im Callstack befindet sich in der atlthunk.dll

%Vor%

Die geworfene Ausnahme im Debugger wird angezeigt als:

%Vor%

oder ein anderes Beispiel

%Vor%

Was ich über atlthunk.dll weiß:

Atlthunk.dll scheint nur Teil des 64-Bit-Betriebssystems zu sein. Ich habe es auf einem Win 8.1 und Win 10 Systemen gefunden.

Wenn atlthunk.dll verfügbar ist (alle Windows 10-Rechner), kümmert sich diese DLL um das Thunking. Wenn die DLL nicht vorhanden ist, wird das Thunking auf die übliche Weise ausgeführt: Zuweisen eines Blocks zum Heap, Markieren als ausführbar, Hinzufügen von Lade- und Sprunganweisungen.

Wenn die DLL vorhanden ist. Es enthält 256 vordefinierte Slots für Unterklassen. Wenn 256 Unterklassen fertig sind, lädt sich die DLL ein zweites Mal in den Speicher und verwendet die nächsten 256 verfügbaren Steckplätze in der DLL.

Soweit ich sehe, gehört die atlthunk.dll zu Windows 10 und ist nicht austauschbar oder verteilbar.

Geprüfte Dinge:

  • Antivirus-System wurde ein- oder ausgeschaltet, keine Änderung
  • Der Schutz der Datenausführung spielt keine Rolle. (/ NXCOMPAT: NEIN und die EXE ist als Ausschluss in den Systemeinstellungen definiert, stürzt auch ab)
  • Zusätzliche Aufrufe an FlushInstructionCache oder Sleep-Aufrufe nach der Unterklasse haben keine Auswirkungen.
  • Heap-Integrität ist hier kein Problem, ich habe es mit mehr als einem Tool überprüft.
  • und ein paar Tausend mehr (ich habe vielleicht schon vergessen, was ich getestet habe) ...;)

Reproduzierbarkeit:

Das Problem ist irgendwie reproduzierbar. Es stürzt nicht ständig ab, es stürzt wahllos ab. Ich habe eine Maschine, bei der der Code bei jeder dritten Ausführung abstürzt.

Ich kann es auf zwei Desktop-Stationen mit i7-4770 und einem i7-6700 abbilden.

Andere Maschinen scheinen überhaupt nicht betroffen zu sein (funktioniert immer auf einem Laptop i3-3217 oder Desktop mit i7-870)

Über das Beispiel:

Der Einfachheit halber verwende ich einen SEH-Handler, um den Fehler zu finden. Wenn Sie die Anwendung debuggen, zeigt der Debugger den oben erwähnten Callstack an. Das Programm kann mit einer Ganzzahl in der Befehlszeile gestartet werden. In diesem Fall startet das Programm erneut mit der Zählung um 1.So dekrementiert, wenn Sie CrashAtlThunk 100 starten, wird die Anwendung 100 Mal gestartet. Bei einem Fehler fängt der SEH-Handler den Fehler auf und zeigt den Text "Crash" in einem Meldungsfeld an. Wenn die Anwendung ohne Fehler ausgeführt wird, zeigt die Anwendung in einem Meldungsfeld "Succeeded" an. Wenn die Anwendung ohne Parameter gestartet wird, wird sie nur einmal ausgeführt.

Fragen:

  • Kann jemand anderes dies repro-duzieren?
  • Hat jemand ähnliche Effekte gesehen?
  • Weiß jemand oder kann er sich einen Grund dafür vorstellen?
  • Weiß jemand, wie man dieses Problem umgehen kann?

Hinweise:

2017-01-20 Support-Fall bei Microsoft geöffnet.

Der Code

%Vor%

Kommentar nach der Antwort von Eugene (24. Februar 2017):

Ich möchte meine ursprüngliche Frage nicht ändern, aber ich möchte einige zusätzliche Informationen hinzufügen, wie man dies in eine 100% Repro bringt.

1, Ändern Sie die Hauptfunktion zu

%Vor%
  1. Heben Sie die Entgegennahme des LoadLibrary-Aufrufs auf. Kompilieren.

  2. Führen Sie das Programm einmal aus und stoppen Sie es im Debugger. Notieren Sie die Adresse, unter der die Bibliothek geladen wurde (hMod).

  3. Stoppen Sie das Programm. Kommentieren Sie nun den Library-Aufruf erneut und ändern Sie den VirtualAlloc -Aufruf auf die Adresse des vorherigen hMod-Wertes, dies ist die bevorzugte Ladeadresse in dieser Fenstersitzung.

  4. Kompilieren Sie erneut und führen Sie es aus. CRASH!

Danke an eugene.

Bis jetzt. Microsoft untersucht das immer noch. Sie haben Dumps und alles Code. Aber ich habe keine endgültige Antwort. Tatsache ist, dass wir in einigen Windows 64bit OS einen fatalen Fehler haben .

Ich habe die folgenden Änderungen vorgenommen, um dies zu umgehen

  1. Öffnen Sie atlstdthunk.h von VS-2015.

  2. Entfernen Sie den Block #ifdef vollständig, der USE_ATL_THUNK2 definiert. Codezeilen 25 bis 27.

  3. Kompilieren Sie Ihr Programm erneut.

Dies ermöglicht den alten Thunking-Mechanismus, der von VC-2010, VC-2013 ... bekannt ist, und dies funktioniert für mich ohne Absturz. Solange keine anderen bereits kompilierten Bibliotheken beteiligt sind, die 256 Fenster über ATL in irgendeiner Weise ableiten oder verwenden können.

Kommentar (1. März 2017):

  • Microsoft hat bestätigt, dass dies ein Fehler ist. Es sollte in Windows 10 RS2 behoben werden.
  • Mircrosoft stimmt zu, dass das Bearbeiten der Header in atlstdthunk.h eine Umgehung für das Problem darstellt.

Tatsächlich sagt das. Solange es keinen stabilen Patch gibt, kann ich nie wieder den normalen ATL-Thunking verwenden, weil ich nie wissen werde, welche Windows-Versionen auf der Welt mein Programm benutzen werden. Weil Windows 8 und Windows 8.1 und Windows 10 vor RS2 unter diesem Fehler leiden werden.

Letzter Kommentar (9. März 2017):

  • Builds mit VS-2017 sind ebenfalls betroffen, es gibt keinen Unterschied zwischen VS-2015 und VS-2017
  • Microsoft hat entschieden, dass es in diesem Fall keine Lösung für ältere Betriebssysteme geben wird.
  • Weder Windows 8.1, Windows Server 2012 RC2 oder andere Windows 10-Builds erhalten einen Patch, um dieses Problem zu beheben.
  • Das Problem ist zu selten und der Einfluss für unser Unternehmen ist zu klein. Auch die Reparatur von unserer Seite ist zu einfach. Andere Berichte über diesen Fehler sind nicht bekannt.
  • Der Fall ist geschlossen.

Mein Rat für alle Programmierer: Ändere das atlstdthunk.h in deiner Visual Studio Version VS-2015, VS-2017 (siehe oben). tue ich nicht verstehe Microsoft. Dieser Fehler ist ein schwerwiegendes Problem beim ATL Thunking. Es kann jeden Programmierer treffen, der eine größere Anzahl von Fenstern und / oder Unterklassen verwendet.

Wir kennen nur eine Korrektur in Windows 10 RS2. Also alle älteren OS sind betroffen! Daher empfehle ich, die Verwendung der atlthunk.dll durch Auskommentieren der oben genannten Definition zu deaktivieren.

    
xMRi 19.01.2017, 12:08
quelle

1 Antwort

4

Dies ist der Fehler in atlthunk.dll. Wenn es zum zweiten Mal und später geladen wird, geschieht dies manuell über den MapViewOfFile-Aufruf. In diesem Fall wird nicht jede Adresse in Bezug auf die Modulbasis richtig geändert (wenn DLL geladen durch LoadLibarary / LoadLibraryEx Aufrufe System Loader tut dies automatisch). Wenn dann die erste Zeit-DLL auf bevorzugte Basisadresse geladen wurde, funktioniert alles einwandfrei, da unveränderte Adressen auf den ähnlichen Code oder die gleichen Daten zeigen. Aber wenn nicht, stürzt man ab, wenn das 257. Unterklasse-Fenster Nachrichten verarbeitet.

Seit Vista haben wir die Funktion "Adressraum-Layout-Randomisierung", die erklärt, warum Ihr Code zufällig abstürzt. Absturz jedes Mal, wenn Sie atlthunk.dll Basisadresse auf Ihrem Betriebssystem (es unterscheidet sich bei verschiedenen Betriebssystemversionen) finden und eine Speicherseite Adressraum Reservierung an dieser Adresse mit VirtualAlloc Aufruf vor der ersten Unterklasse . Um die Basisadresse zu finden, können Sie den Befehl dumpbin /headers atlthunk.dll verwenden oder PE-Header manuell analysieren.

Mein Test zeigt, dass unter Windows 10 die Version 14393.693 x32 betroffen ist, aber x64 nicht. Auf dem Server 2012R2 mit den neuesten Updates sind beide (x32 und x64) Versionen betroffen.

BTW, atlthunk.dll-Code hat etwa 10 mal mehr CPU-Anweisungen pro Thunk-Aufruf als vorherige Implementierung. Es kann nicht sehr wichtig sein, aber es verlangsamt die Verarbeitung der Nachricht.

    
Eugene 23.02.2017, 09:34
quelle