2010-12-30 4 views
0

Ich habe einen Absturz, der mich verwirrt und den ich bis jetzt für unmöglich gehalten habe, konsistent zu reproduzieren. Der Code wird mit Visual Studio erstellt 2008.C++ Programmabsturz (C0000005 Zugriffsverletzung) bei Aufruf

Die (vereinfachte, natürlich) Quellcode sieht wie folgt aus:

class AbstractParentClass 
{ 
private: 
    /* data members */ 
public: 
    AbstractParentClass(); 
    /* 
     virtual functions ... 
    */ 
}; 

class ChildClass : public AbstractParentClass 
{ 
private: 
    /* data members */ 
public: 
    ChildClass(); 
    /* 
     overridden/implemented virtual functions ... 
    */ 
}; 

void DifferentClass::func(const char ** strs) 
{ 
    ChildClass child_class; 
    int i = 0; 
    [...] 
} 

Die Demontage von der Crash-Dump wie folgt aussieht:

Library!DifferentClass::func: 
612cab20 83ec58   sub  esp,58h 
612cab23 56    push esi 
612cab24 57    push edi 
612cab25 8bf9   mov  edi,ecx 
612cab27 8d4c2420  lea  ecx,[esp+20h] 
612cab2b e8e053403f  call a06cff10 
612cab30 8b742464  mov  esi,dword ptr [esp+64h] 
[...] 

Ordnen Sie die Quelle von func() gegen die Demontage an, es sieht aus wie folgt:

In einem erfolgreichen Lauf (andere Maschine, obwohl auf der gleichen Maschine, der Absturz ist nicht zuverlässig reproduzierbar), der einzige Unterschied in der Disassemblierung ist die Aufrufanweisung, die korrekt auf die Adresse des Standardkonstruktors von ChildClass Mapping, dies wie:

00404e8b call  ChildClass::ChildClass (40a3d0h) 

statt wie:

612cab2b call  a06cff10 

so bei dem Absturz läuft, dass a06cff10 Adresse, die als Call-Anweisung des Parameter dient dazu, zu sein scheint kommend wer weiß, wo und ist nicht auf etwas Bestimmtes abgebildet. Und so, wie zu erwarten, Zugang versucht, die Adresse (an die Child Standardkonstruktors zu erhalten) wird, was zu einer Zugriffsverletzung:

EXCEPTION_RECORD: 0012f688 -- (.exr 0x12f688) 
ExceptionAddress: a06cff10 
    ExceptionCode: c0000005 (Access violation) 
    ExceptionFlags: 00000000 
NumberParameters: 2 
    Parameter[0]: 00000000 
    Parameter[1]: a06cff10 
Attempt to read from address a06cff10 

Jeder Versuch, an dieser Adresse in dem Crash-Dump zu sehen gibt in der Tat, dass die Adresse aus ist Grenzen für den Prozess.

UPDATE: Also nach dem Lesen der Antwort unten von zvrba und schaut es weiter an, scheint der problematische Aufruf der erste von einem Dutzend Funktionsaufrufe in einer statischen Bibliothek zu sein (die wiederum von einer DLL geladen wird) alle haben einen falschen Funktionsoffset. Sie sind nicht alle Funktionen in derselben Klasse. Es gibt drei oder vier verschiedene Klassen mit betroffenen Funktionen, obwohl alle Klassen (die aufgerufen werden und aufgerufen werden) in derselben statischen Bibliothek existieren. In diesem ersten Aufruf, der Dinge abstürzte, war der Befehl e8e053403f und der 3F4053E0-Offset in diesem Befehl sollte einen Offset von nur 53E0 haben. Alle anderen Instanzen haben das gleiche Offset-Problem. Der Offset in der Anweisung ist 3F40XXXX, wenn er nur XXXX sein sollte. Der extra 3F400000 schickt natürlich Dinge in Never Never Land. Bisher habe ich kein Muster aufgegriffen, hinsichtlich welcher Funktionsadressen innerhalb der Demontage gültig sind und welche nicht. Eine Memberfunktion von DifferentClass in der Bibliothek hat alle Aufrufe von ChildClass als fehlerhaft, während eine andere Memberfunktion von DifferentClass einen anderen Aufruf in ChildClass hat.

Hat jemand da draußen so etwas gesehen/irgendwelche Gedanken über mögliche Ursachen davon?

+2

Vermissen Sie die Initialisierung einer Klassenmitgliedsvariablen? – GWW

+0

Ist es möglich, dass etwas 3f schreibt, um zur Laufzeit 612cab2f zu adressieren? – TonyK

+0

Schalten Sie die Warnstufe hoch: W4 auf VS oder -Wall -Wextra -pedantisch auf gcc dann stellen Sie sicher, dass es mit 0 Warnungen kompiliert. –

Antwort

0

Nicht, dass dies hilft, aber wo ich arbeite, behebt ich ein Access Violation Fehler Ergebnis vom Zugriff auf eine Out-of-Grenzen-Array in einem C-Programm. Ich konnte es nur ab und zu auf meiner Maschine reproduzieren. Die einzige Lösung, die wir tun konnten, bestand darin, eine große Anzahl von "Array-außerhalb-der-Grenzen" -Überprüfungen durchzuführen.

2

Haben Sie vielleicht Kindkonstruktor in einer anderen DLL implementiert? Was ich vermute, ist, dass die DLL im Crash-Lauf an einer anderen Adresse als ihrer bevorzugten Adresse geladen wird - das können Sie im Modulfenster im VS-Debugger überprüfen.Dies führt wiederum dazu, dass ein Rufziel falsch berechnet wird (diese bestimmte Rufanweisung ist relativ). Der Offset in der Assembly (4 Bytes nach dem E8-Opcode) ist ebenfalls sehr seltsam und sieht eher wie eine Verschiebung aus, die nicht behoben wurde, als ein gültiger Offset. Wie lädst du diese DLL?

+0

Hmm, danke für diesen Input. Um die Frage nach der Gesamtstruktur zu beantworten: "Library" ist eine DLL, die zur Laufzeit von der App geladen wird. "Library" wiederum konsumiert eine * statische * Utility-Bibliothek, in der es zu verschiedenen Zeiten verschiedene Aufrufe durchführt, und ParentClass, ChildClass und DifferentClass befinden sich alle in den Innereien der statischen Bibliothek. Betrachtet man die Disassemblierung des Absturzes, sehe ich, dass tatsächlich alle Aufrufe von ChildClass-Methoden, die aus DifferentClass :: func() stammen, ähnlich schlecht aussehen. Aber nur in DifferentClass :: func(). Andere Aufrufe von ChildClass von anderen Methoden in DifferentClass sehen gut aus. – Michael

+0

Wenn Sie DifferentClass :: func() genauer betrachten, enthält es Aufrufe in zwei andere Klassen außer ChildClass, die ebenfalls in derselben statischen Bibliothek enthalten sind. Die Disassemblierung zeigt an, dass die mehreren Aufrufe in eine dieser beiden anderen Klassen gut sind, aber der einzige Aufruf an einen anderen ist schlecht, genau wie alle Aufrufe von ChildClass. – Michael

+0

Angesichts Ihrer Bemerkung über den seltsam aussehenden Offset, habe ich mich dort genauer angeschaut und tatsächlich besteht bei allen schlechten Aufrufen eine Konsistenz darin, dass der Offset in der Aufrufanweisung XXXX403F ist, wobei * XXXX0000 sein sollte. ("XXXX" ist die gleichen zwei Bytes in sowohl dem falschen Offset als auch seinem entsprechenden richtigen.) – Michael

2

Es ist schwer zu verstehen, was mit dem größten Teil des Quellcodes passiert, obwohl aus Ihren Kommentaren und der Disassemblierung ersichtlich ist, dass die Adresse der ChildClass-Vtable beschädigt ist. Dies hätte mehrere mögliche Ursachen, z.B.

  • @contactmatt Wie erwähnt, array/Puffer Überschreitungen
  • mit einem zerstörten Objekt
  • eine unitialized Variable
  • falsch Gießen/Umwandeln einer variablen/pointer
  • Laden von Daten aus einem Puffer in einen Objekt ohne Verpackung/die Byte-Überprüfung Ausrichtungen
  • usw.

Suchen Sie zuerst die vtable-Adresse und versuchen Sie, durch den Debugger zu gehen und zu prüfen, wann der vtable-Speicher überschrieben wird. Es ist wahrscheinlich ein paar Bytes über der Stelle (40a3d0h) Sie wies darauf hin:

call  ChildClass::ChildClass (40a3d0h) 

Dann überprüfen Sie den Code, der während diesem Zeitpunkt werden die Ausführung könnte.

Vorbehalt: Weil die Ursache ziemlich unbekannt ist, würde das Finden der tatsächlichen Reparatur bedeuten, durch viel Code zu lesen/durch Ihre Quellensteuerungsversionen zu gehen, um mögliche Gefahren zu sehen. Aus Erfahrung, das Problem (z. B. eines der genannten) verursacht die Korruption möglicherweise nicht einmal irgendwo in der Nähe der Codezeile mit der Zugriffsverletzung.

+0

Danke. In der Tat würde ich gerne vorsichtig durch den Absturz gehen, aber leider kann ich es nicht zur Wiederholung bringen. So habe ich zur Zeit keine Gelegenheit, Dinge zu beobachten, die schief laufen. – Michael