Servus, es klemmt mal wieder. Ich bekomme die Fehlermeldung nach fork() und Ausführen des Threads unmittelbar bei oder vor exit( 0);. Alle free() in allen Dateien sind durch eine Abfrage ob der pointer != NULL ist abgesichert. Im Web gibt es einige Seiten die auf ein Problem mit dem Thread-Cache hinweisen - so ganz blicke ich da aber nicht durch (vor Allem was ich da machen kann). Hat vielleicht jemand eine Idee ? Ubuntu, GCC
Joachim D. schrieb: > Alle free() in allen Dateien sind > durch eine Abfrage ob der pointer != NULL ist abgesichert. unnötig. > free(): double free detected Das bedeutet, dass du free() zwei mal für den selben Pointer ausrufst. Das kann auch jeweils einmal aus zwei Threads passieren. Das ist ein schwerer Fehler und führt zu Undefined Behavior.
Joachim D. schrieb: > Alle free() in allen Dateien sind > durch eine Abfrage ob der pointer != NULL ist abgesichert. Gegen was genau? Ansonsten wird das Problem genau das sein, was die Fehlermeldung besagt. Oliver
fork erzeugt einen neuen Prozess, keinen Thread - gemeinsam genutzter Speicher entfällt also. exit in Kindprozessen ist meist unpassend - üblich ist _exit. Der überflüssige NULL-check vor free ist bereits genannt worden. free wird auch innerhalb der Libraries genutzt - ein doppelter fclose z.B. erzeugt auch so eine Meldung.
Joachim D. schrieb: > Alle free() in allen Dateien sind > durch eine Abfrage ob der pointer != NULL ist abgesichert Das funktioniert aber nur wenn du den Pointer auch irgendwann auf Null setzt. Dein free macht das nicht von alleine.
Die free()'s habe ich pingelig kontrolliert, da ist eigentlich nichts. Ein free() auf einen schon freigebenen ptr läßt den Prozess eigentlich nicht crashen. Was bedeutet das tcache2 ? Die Meldung kommt unmittelbar am Programmende, vor oder mit exit() oder _exit().
Ich kann deinen Sourcecode von hier aus nicht erkennen, aber mein erster Gedanke: malloc() vor dem fork()? free() in den beiden Prozessen? Dat geit net. fork() klont dir zwar die Pointer, aber nicht den allozierten Speicher.
Joachim D. schrieb: > Ein free() auf einen schon freigebenen ptr läßt den > Prozess eigentlich nicht crashen. Eben doch. Das ist ein Double-Free. Ein schwerer Speicherfehler. Das ist Undefined Behavior. > Die free()'s habe ich pingelig kontrolliert, da ist eigentlich > nichts. Dann kontrolliere es noch einmal. Oder entferne einfach mal alle free()-Aufrufe und lecke den Speicher. Tritt es immer noch auf? Nein -> Dann wirds wohl einer deiner free()-Aufrufe gewesen sein. Ja -> Dann wirds wohl irgendeine andere Ressource über Bande sein, die du doppelt freigibst.
Norbert schrieb: > fork() klont dir zwar die Pointer, aber nicht den allozierten Speicher. Ehm, doch. Natürlich tut es das. (Genauer gesagt legt es eine COW-Kopie an).
1 | while( 1){ |
2 | size = sizeof( a1); |
3 | fd = accept( s, (struct sockaddr*) &a1, &size); |
4 | |
5 | if ( ( pid = fork()) < 0) { |
6 | printf( "\nfork err %s", strerror( errno)); |
7 | } else { |
8 | if ( pid == 0) { |
9 | close( s); |
10 | DoCmd( fd); |
11 | } else { |
12 | close( fd); |
13 | }
|
14 | }
|
15 | }
|
1 | static int |
2 | DoCmd( |
3 | int fd |
4 | )
|
5 | {
|
6 | int i, st, size, pid; |
7 | char http_header[4096]; |
8 | char *p, *q, ip[256], req[1044], str[2048]; |
9 | char *tmp = NULL; |
10 | |
11 | JA( "****************************** INCOMING DATA %s / %s ************************************", sysdatel(), systime()); |
12 | |
13 | jStartTime(); |
14 | LoadServicesConf(); |
15 | |
16 | strcpy( ip, inet_ntoa( a1.sin_addr)); |
17 | |
18 | // vorher mal Speicher resrvieren, bei GET wird die Länge nicht
|
19 | // übergeben. 16kB reichen für GET.
|
20 | |
21 | if ( (tmp = calloc( 1, 16384)) == NULL) |
22 | ExitError( "GetRecData", "A alloc err tmp"); |
23 | |
24 | size = GetRecData( fd, &tmp, ip); |
25 | memset( http_header, 0, sizeof( http_header)); |
26 | mopen( tmp, strlen( tmp)); |
27 | st = 0; |
28 | while ( mgetstrim( str)){ |
29 | if ( ! *str) |
30 | break; |
31 | appsprintf( http_header, "%s\n", str); |
32 | st += strlen( str); |
33 | }
|
34 | |
35 | printf( "\n %s %-16s %8d Byte ", systime(), ip, size); |
36 | fflush( stdout); |
37 | |
38 | // Kram ist da, also ausführen
|
39 | |
40 | if ( (sfp = fopen( tmpfilename( ip), "w")) == NULL){ |
41 | JA( "open error %s %s", tmpfilename( ip), strerror( errno)); |
42 | exit( 0); |
43 | }
|
44 | |
45 | GetInputData( fd, tmp, size, ip, http_header); |
46 | |
47 | fclose( sfp); |
48 | |
49 | if ( tmp != NULL) |
50 | free( tmp); |
51 | |
52 | // Senden des Ergebnisses
|
53 | |
54 | // SendFileToHTTP( fd, ip);
|
55 | close( fd); |
56 | _exit( 0); |
57 | }
|
(Auszug) tmp bekommt in GetRecData() evtl. per realloc() mehr Platz wenn's ein POST ist.
MaWin schrieb: > Oder entferne einfach mal alle free()-Aufrufe und lecke den Speicher. > Tritt es immer noch auf? Nein -> Dann wirds wohl einer deiner > free()-Aufrufe gewesen sein. Ja -> Dann wirds wohl irgendeine andere > Ressource über Bande sein, die du doppelt freigibst. Habe ich schon probiert (wenn der Prozess endelt sollte er ja eh alles freigeben). Vielleicht ein überzähliges close() ? Was ist dieser tcache 2 ?
Auf stackoverflow gefunden: The Thread Local Cache (tcache) is a performance optimization in glibc. Unfortunately, it comes at the expense of security and make some attacks much easier, as you have since discovered. From https://sourceware.org/glibc/wiki/MallocInternals#Thread_Local_Cache_.28tcache.29 While this malloc is aware of multiple threads, that's pretty much the extent of its awareness - it knows there are multiple threads. There is no code in this malloc to optimize it for NUMA architectures, coordinate thread locality, sort threads by core, etc. It is assumed that the kernel will handle those issues sufficiently well. Each thread has a thread-local variable that remembers which arena it last used. If that arena is in use when a thread needs to use it the thread will block to wait for the arena to become free. If the thread has never used an arena before then it may try to reuse an unused one, create a new one, or pick the next one on the global list. Each thread has a per-thread cache (called the tcache) containing a small collection of chunks which can be accessed without needing to lock an arena. These chunks are stored as an array of singly-linked lists, like fastbins, but with links pointing to the payload (user area) not the chunk header. Each bin contains one size chunk, so the array is indexed (indirectly) by chunk size. Unlike fastbins, the tcache is limited in how many chunks are allowed in each bin (tcache_count). If the tcache bin is empty for a given requested size, the next larger sized chunk is not used (could cause internal fragmentation), instead the fallback is to use the normal malloc routines i.e. locking the thread's arena and working from there.
Ich würde es mal mit Valgrind laufen lassen. Der sagt dir höchstwahrscheinlich genau mit Backtrace, wo dein Fehler ist.
valgrind ? Was ist denn das ? (bin noch ziemlich neu in Linux)
Ein sehr mächtiger Speicherfehler-Checker. Wird bei praktisch allen Distributionen als Paket mitgeliefert.
Joachim D. schrieb: > Auf stackoverflow gefunden: Das sind glibc-Internas, die nichts mit deinem Problem zu tun haben Die ändern überhaupt nichts daran, daß dein Code Speicher mehrfach freigibt. Das fällt halt dort auf. Was du auch mal überprüfen könntest, ist, ob die Arrays überlaufen. Das könnte tmp überschreiben. Oliver
Also Folgefehler ... kann sein. Ich schau nochmal alles durch. valgrind schau ich mir mal an. Vielen Dank schon mal.
Joachim D. schrieb: > valgrind schau ich mir mal an. https://valgrind.org/docs/manual/quick-start.html#quick-start.mcrun Einfach nur ein paar Zeichen vor den Programmaufruf. Und dann hat man oft länger was an den Meckereien zu knabbern ;)
Georg A. schrieb: > https://valgrind.org/docs/manual/quick-start.html#quick-start.mcrun Das muß ich nur irgendwie über http zum Laufen kriegen oder den Kram von der cmdline aufrufen ... Wir schon gehen ;)
Joachim D. schrieb: > Das muß ich nur irgendwie über http zum Laufen kriegen Was hat das mit HTTP zu tun? Du musst lediglich statt `meinprogramm` nun `valgrind meinprogramm` starten.
Ist ein Webserver. Keine Kommandozeile. Habe eine zum Test eingebaut. ==6731== Memcheck, a memory error detector ==6731== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==6731== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==6731== Command: ../../bin/uquery.x --test ==6731== ==6736== Invalid read of size 4 ==6736== at 0xA3BECFB: fclose@@GLIBC_2.2.5 (iofclose.c:48) ==6736== by 0x10F766: DoCmd (httpsrv.c:177) ==6736== by 0x10F46F: HttpServer (httpsrv.c:115) ==6736== by 0x12CD26: main (uquery.c:124) ==6736== Address 0xa58cbb0 is 0 bytes inside a block of size 472 free'd ==6736== at 0xA32127F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==6736== by 0xA3BEDA6: _IO_deallocate_file (libioP.h:862) ==6736== by 0xA3BEDA6: fclose@@GLIBC_2.2.5 (iofclose.c:74) ==6736== by 0x112F73: GetInputData (input.c:201) ==6736== by 0x10F757: DoCmd (httpsrv.c:175) ==6736== by 0x10F46F: HttpServer (httpsrv.c:115) ==6736== by 0x12CD26: main (uquery.c:124) ... ==6731== LEAK SUMMARY: ==6731== definitely lost: 0 bytes in 0 blocks ==6731== indirectly lost: 0 bytes in 0 blocks ==6731== possibly lost: 0 bytes in 0 blocks ==6731== still reachable: 2,525 bytes in 7 blocks ==6731== suppressed: 0 bytes in 0 blocks ==6731== Reachable blocks (those to which a pointer was found) are not shown. ==6731== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==6731== ==6731== For lists of detected and suppressed errors, rerun with: -s ==6731== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Schaut so aus als wäre das Problem, daß ich einen globalen Filehandle FILE* verwende, ich vermute mal der sollte nur in dem angeschubsten Prozess gültig sein. Ich baue das halt um ;( Die Ausgabe habe ich gekürzt, sonst kommt formerror_text_long
Ja, das war's ;) Die Meldung ist weg. Vielen Dank nochmal an alle ! Ich hatte ein file dessen FILE* global definiert war, in dem neuen Prozess geöffnet und später dann wieder geschlossen. Das mußte krachen ... Was das tcache2 damit zu tun hat ? Ich liebe C
So was macht man nicht selbst sondern verwendet Bibliotheken. Dann kann man sich mit der Anwendung beschäftigen und nicht mit Elementaren Kram. Der dir so wie es ausschaut sehr früh auf die Füße fällt. https://www.gnu.org/software/libmicrohttpd/manual/libmicrohttpd.html Als Beispiel welches sehr brauchbar ist....
:
Bearbeitet durch User
Marco H. schrieb: > So was macht man nicht selbst sondern verwendet Bibliotheken. Dann kann > man sich mit der Anwendung beschäftigen und nicht mit Elementaren Kram. > Der dir so wie es ausschaut sehr früh auf die Füße fällt. ... und dann implodiert einem halt das Framework irgendwann und man hat keine Ahnung, wie man überhaupt rausfindet, wo und warum das sein könnte. Wenn man selber Unfug macht, muss das ja nicht unbedingt im eigenen Code auffallen. Nene, so ein eigenes Gebastel und das Lernen von Grundlagen des Debuggings sind schon wichtig.
Georg, ich sehe das auch so. Ich bin mit diesen "Fertig-all-inclusive-Sachen" schon ziemlich auf die Schnauze geflogen. Nicht nochmal ;)
Georg A. schrieb: > Nene, so ein eigenes Gebastel und das Lernen von Grundlagen des > Debuggings sind schon wichtig. Ja, ist schon wichtig die remote-exploitable Schwachstelle selbst einzubauen, wenn man sich seinen eigenen Webserver in C bastelt.
1 | char http_header[4096]; |
2 | ...
|
3 | appsprintf( http_header, "%s\n", str); |
Ob da ein Überlauf möglich ist? Keine Ahnung. Aber es riecht stark nach Fisch.
Joachim D. schrieb: > Alle free() in allen Dateien sind > durch eine Abfrage ob der pointer != NULL ist abgesichert. Das allein reicht aber IMO nicht, weil es eine race condition gibt. Pointer ist ungleich null, Thread 1 kommt durch die if-Abfrage, macht free(), wird aber vor dem manuellen Nullen des Pointers wegscheduled. Thread 2 kommt deswegen auch durch die if-Abfrage, es kommt zum double free.
MaWin schrieb: > Ob da ein Überlauf möglich ist? Keine Ahnung. Aber es riecht stark nach > Fisch. Ist nur aus Testzwecken drin, fliegt später wieder raus.
Joachim D. schrieb: > Georg, > > ich sehe das auch so. > > Ich bin mit diesen "Fertig-all-inclusive-Sachen" schon ziemlich auf > die Schnauze geflogen. Nicht nochmal ;) Zuerst schaut man sich den Vorschlag an, dann wird man Festellen das der Vorschlag gar nicht so dumm war. HTTP ist mehr als man denkt. Sein Kram wird jenseits von GET an den modernen Browsers schon scheitern. Wenn die Daten größer werden und der Browser anfängt mit Chunks ist sein Latein sowie so zu ende. Ich sehe das es jetzt schon der Fall ist, wenn man nur mal so in das HTTP Protokoll schaut welche Anforderungen selbst bei simplen Anfragen GET Anfragen zu erfüllen sind. Nur den Header auseinander zu nehmen, wird ich ohne Bibliotheken nicht mehr alleine aufsetzen wollen. Das mag vor 20 Jahren aufgrund von alternativen und der noch Einfachheit des Protokolls anderes gewesen sein. Das läuft vom Ansatz her gegen den Baum, ohne die Endanwendung zu betrachten.
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.