Stack corruption
-
Hallo
Ich war sehr lange nicht mehr hier.
Nun habe ich aber ein C Problem bei dem ich nicht weiter kommt.Mein Programm besteht aus 8 Threads und diversen libs. Ist also nicht sehr übersichtlich. OS ist Linux, Arch Arm6
Nun habe ich das Problem das irgend jemand mir hin und wieder Pointer überschreibt oder sogar den Call Stack corrupiert.
In beiden Fällen gibt es einen Segmentation fault (core dumped)Wenn ich den Coredump analysiere sehr ich entweder ein Pointer auf 0x65 steht oder der Call Stack nicht richtig aufgelöst werden kann (Beispiel cannot access memory at address 0x3)
Eigentlich würde ich Valgrind laufen lassen. Leider gibt es Valgrind nicht für meine Architektur.
Ich habe schon duma ausprobiert aber das bleibt irgendwann stehen.Nun will ich es selber ohne Tools lösen.
Mein Ansatz ist folgender:
void *mymalloc (size_t size, char *file, unsigned short line) { void *mem; mem = malloc(size + 20); if (mem != NULL) { char *s; memset (mem, 0xff, 1); memcpy (mem+1, &line, 2); for (s = file + strlen (file); s != file; s--) { if (*s == '\\' || *s == '/') { s++; break; } } strncpy ((char *) mem + 3, s, 17); return (void *) ((char *) mem + 20); } return NULL; } void myfree (void *ptr) { if (ptr != NULL) { if ( *((char *) ptr - 20) != 255 ) { printf("\n\nMEMORY IS CURRUPT\n"); printf("%s %d %d\n\n", ((char *) ptr - 17), *((char *) ptr - 19), *((char *) ptr - 18)); } free ((char *) ptr - 20); } }
Andere Speicheralloziierende Fkts dann natürlich auch.
Nun sollte ich eigentlich sehen welche Variable vor dem corrupten Speicher steht.
Schützt mich aber leider nicht vor dem überschreiben selber, sondern es sollte mir nur helfen im Fehlerfall raus zu finden wer da was überschrieben hat.Daher habe noch einen anderen Ansatz:
void *mymalloc( size_t size ) { char *buf; int pagesize = sysconf(_SC_PAGE_SIZE); // int prot = PROT_READ | PROT_WRITE; /* Default buffer protection */ int prot = PROT_NONE; /* Default buffer protection */ /* Allocate memory, reserve space for a full page */ buf = malloc( size + pagesize - 1 + pagesize); if( !buf ) return NULL; /* Failed to allocate memory */ /* Align pointer to a multiple of PAGESIZE */ buf = (char *)(((int) buf + pagesize - 1 ) & ~( pagesize - 1 )) + pagesize - size; mprotect( buf+size, pagesize, prot ); return buf; }
Nun knallt es bereits wenn jemand über die size rüber schreibt.
Nachteil ist das ich nun zwei mal Pagesize mehr alloziiere. Wenn ich also 100 Bytes haben will, dann alloziierte ich in dem Fall zwei mal 4096 bytes.
Beim freigeben muss man dann natürlich aufpassen das man alles wieder frei gibt.
Muss ich eigentlich die protection dann wieder aufheben oder passiert das automatisch?
In meinen Beispiel kann ich natürlich nur overrun überprüfen.
Wahllose Zugriffe oder underrun finde ich so nicht.Hat das schon mal jemand so gemacht und kann mir noch weitere Tipps geben? Habe die Stelle noch nicht gefunden.
-
Wenn er dir den Stack zerhaut, wird es dich nicht viel weiterbringen, die Funktionen für Heap-Allokation zu ersetzen.
Was der gcc auch auf ARM können sollte, ist mudflap; damit generiert der Compiler Code, der Arraygrenzen etc. zur Laufzeit prüft. Kompilier den Code mal mit -fmudflap -lmudflap und kuck, ob sich das Programm dann zur Laufzeit beschwert. Unter Umständen musst du dafür neben dem Compiler ein weiteres Paket installieren (unter Debian libmudflap0-$VERSION-dev). Jedenfalls muss libmudflap auf deinem System vorhanden sein, damit das funktioniert.
-
Danke, das werde ich mir mal ansehen.
Wie kann es den aber sein das scheinbar der Stack corrupt ist?
Wie kann das passieren?Ziemlich unwahrscheinlich das ein Pointer eine Rücksprungadresse überschreibt oder?
-
decembersoul schrieb:
Ziemlich unwahrscheinlich das ein Pointer eine Rücksprungadresse überschreibt oder?
Nein überhaupt nicht. Vermutlich rechnest du nämlich damit direkt oder indirekt (Arrayzugriff!) herum und schreibst dann in Speicherbereiche, in denen du nichts zu suchen hast.
Beim ersten Versuch bekomme ich noch:
undefined reference to `__mf_check'Hast du denn auch die mudflap-Linkeroption beim Linken mit angegeben?
-
Habe mudflap am laufen.
Bricht nach einer Zeit mit einem Assert ab.mf-runtime.c: 1164: __mf_register: Assertion `rc==0' failed.
Bis dahin gab es aber schon ca 1500 mudflap violations.
Aber ich vermute das es alles die selben sind.
vsnprintf format
Da sird wohl irgendwo ein Parameter fehlen.Suche aber erst mal weiter.
-
decembersoul schrieb:
Habe mudflap am laufen.
Bricht nach einer Zeit mit einem Assert ab.mf-runtime.c: 1164: __mf_register: Assertion `rc==0' failed.
Bis dahin gab es aber schon ca 1500 mudflap violations.
Aber ich vermute das es alles die selben sind.
vsnprintf format
Da sird wohl irgendwo ein Parameter fehlen.Suche aber erst mal weiter.
Hast du mal daran gedacht, den Code hier zu zeigen? Fehler bei den Parametern sollte der Compiler dir anwarnen, wenn du die Warnoptionen auf Maximum setzt (was man immer tun sollte). Klingt eher so als wäre das Zielarray zu klein.
edit: Ok, bei vsnprintf wird der Compiler schwerlich warnen können.
-
Ich kann schlecht das ganze Projekt hier rein posten.
Wenn ich eine Stelle in Verdacht habe, dann poste ich es hier gerne rein.Die Stelle mit vsnprintf habe ich erst mal rausgenommen. Denke aber nicht das es das eigentliche Problem ist.
-
Bin zwar nicht weiter aber es interessiert mich gerade trotzdem warum vsnprintf bei mir ein Fehler sein könnte und wie ich es besser machen könnte.
void logEvent(int iLogLevel, const char* const pcFileName, int iLinenumber, const char* const pcFunctionName, int iPid, const char* const fmt, ...) { va_list Args; char cBuffer[MAX_LOG_BUFFER_SIZE]; char cBufferMessage[MAX_LOG_MESSAGE_BUFFER_SIZE]; char *pBuf = cBuffer; int i= 0; memset(cBuffer, '\0', sizeof(cBuffer)); va_start(Args, fmt); vsnprintf(cBufferMessage, MAX_LOG_MESSAGE_BUFFER_SIZE, fmt, Args); va_end(Args); ... }
Bei Printf kann der gcc ja auch überprüfen ob die Anzahl der Parameter stimmt. Wie kann ich das hier erreichen?
Oder muss ich selber die Args und die "%" zählen?
-
Das Codestück sieht erst einmal unverdächtig aus. Meistens haben solche fehler ihre Ursachen auch früher als man das Problem überhaupt bemerkt. Guck also wirklich die erste Warnmeldung an die du bekommst, nicht die häufigste. Wenn ich deine Funktion zu etwas compilierbarem umschreibe und aufrufe, dann funktioniert sie jedenfalls.
Falls die Argumente nicht passen: Keine Chance, das zur Compilezeit zu prüfen. Das ist hier eine ganz allgemeine Funktion, die alles bekommen könnte, da kann nichts geprüft werden. Wenn natürlich die Argumente nicht passen, dann kannst du hier leicht ein Problem bekommen. Das ist eben einer der Nachteile von C, dass man diese Art Fehler erst zur Laufzeit bemerkt. Du kannst ja mal spasseshalber das vsnprintf rausmachen und dir den Formatstring und die Anzahl der Argumente ausgeben, dann kannst du zumindest mal grob prüfen, ob wenigstens das passt. Ob der Typ der Argumente passt ist aber so gut wie unmöglich zu prüfen.
Was du jedoch tun kannst: Beim GCC kannst du angeben, dass deine Funktion einen printf-artigen Formatstring entgegen nimmt:
http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html
Dann kann der Compiler beim Aufruf deiner Funktion prüfen, ob die Argumente zum Format passen (sofern das Format eine Compilezeitkonstante ist) und andernfalls, wie von printf & Co. gewöhnt, eine Warnung schmeißen.
-
Danke für die Info!
So ich habe den Test mal eingebaut und ich bekomme wirklich einige Fehler a
warning: format ‘%d’ expects type ‘int’, but argument 10 has type ‘const char *’
warning: too many arguments for format
warning: format ‘%E’ expects type ‘double’, but argument 10 has type ‘char *’Baue die alle mal aus aber ich habe nicht das Gefühl das es mein initiales Problem mit dem Stack lösen wird.
-
Meinst du nicht, dass du bevor du dich diffizilen dynamischen Problemen in Multithreadumgebungen widmest, erstmal die groben Bugs die der Compiler dir schon mitteilt, beseitigen solltest?
Armv6/Linux wird doch durch ein valgrind supported, oder was meinst du damit, dass du nichts dazu gefunden hast?
http://sourceforge.net/mailarchive/forum.php?thread_name=201010211806.40304.jseward%40acm.org&forum_name=valgrind-announce
-
Er versteht die Asserts nicht.
Er versteht die Warnings nicht.
Also ich bin der Meinung, er ist in einer Multithreading-Umgebung so gut auf gehoben, dass er sich nicht mehr um solche Nebensächlichkeiten kümmern muss.
-
@TS
Der Tip vom Wutz Valgrind ist nur was für Weicheier.
Ignorieren, genauso wie die Warnings.
-
@Wutz
Danke für den Hinweiß. Scheinbar bin ich echt blind den in der Announce und auf der Homepage finde ich nur infos zu Armv7 und nicht Armv6. Werde da aber gerne noch mal weiter suchen.@noergel
Nicht wirklich hilfreich!
Klar habe ich Warnings beseitigt und auch versucht raus zu finden warum der Assert kommt. Scheint ein Bug in meiner libmudflap Version zu sein.
In der nächsten Version haben sie da einiges geändert.
Bin leider auf eine gcc Version festgenagelt. Aber mal sehen ob ich die libmudflap alleine hochziehen kann.
-
Klar. Wenn der Debugger einen Fehler anzeigt, dann ist der Fehler bestimmt im Debugger
.
-
Dann nehm ich alles zurück
-
SeppJ schrieb:
Klar. Wenn der Debugger einen Fehler anzeigt, dann ist der Fehler bestimmt im Debugger
.
So habe ich es natürlich nicht gemeint!
Wenn ich so eine Meldung bekomme, dann suche ich natürlich als erstes danach was sie mir sagen soll:
mf-runtime.c: 1164: __mf_register: Assertion `rc==0' failed.Finde dazu nicht sehr viel nur das es dazu einen fix bzw patch für die lib gab.
-
Da ist Mudflap beim Thread-Locking durcheinander gekommen. Eigentlich sollte so etwas nicht passieren; wenn du allerdings vorher schon über tausend Speicherzugriffsfehler hast, kann an der Stelle niemand mehr für definiertes Verhalten garantieren. Räum den Kram erstmal auf, bevor du dich damit herumschlägst.
-
Hat etwas gedauert aber ich habe nun eine neuere gcc Version gebaut und damit beendet sich mein Program nicht mit dem Assert.
Leider tut es nun nicht mehr das was es immer gemacht hat.
Bleibt irgendwann einfach stehen.
Wie geschrieben sind die paar vsnprintf Fehler draußen.
Waren nur wenige Aufrufe die aber immer wieder gemacht wurden. Daher die hohe Anzahl.
Habe jetzt nur den Sprung von gcc 4.3.3 auf 4.3.6 gemacht.
Sollte ich noch etwas Zeit haben, mache ich noch einen Test mit einer recht aktuellen Version.
Hoffe nur das es dann keine Probleme gibt das das system und alle anderen libs mit der 4.3.3 übersetzt wurden.
-
Deine Probleme sind ganz sicher keine Compilerprobleme! Auch der inzwischen etwas ältere GCC 4.3 ist schon sehr sehr standardkonform. Dein Programm ist schlicht und einfach voller Fehler, das kann man sogar über das Internet hinweg merken. Wieso merkst du es nicht?
-
Das nicht der Compiler dafür verantwortlich ist das mein Programm einen Fehler hat steht außer frage. Darum geht es ja auch nicht.
Ihr habt mir nur etwas Hoffnung gemacht das ich den gcc dazu verwenden kann den Fehler zu finden oder wenigstens ein zu kreisen.Mudflap hat mir zwar geholfen einen anderen Fehler zu finden aber das eigentliche Problem ist mir noch unbekannt und ungelöst.
Im Coredump ist der Callstack oft defekt oder irgend ein Pointer ist überschrieben. Oft ist ein Pointer der vorher z.B. auf das Next Element in einer Liste gezeigt hat auf einmal 0x65 und damit kann ich natürlich nicht mehr auf den Pointer zugreifen. Frage mich auch warum immer 0x65.
Meine Vermutung ist das irgend jemand über seine grenzen Schreibt oder einfach irgendwo in den Stack schreibt. Vielleicht liege ich auch ganz falsch.
Ich danke euch auf jeden Fall für die teilweise sehr interessanten und auch guten Tipps.