C++: Segmentation fault in meinem WebsocketServer

OP #3610694
Lesenswert?
• ▲
▼
Für eine grosse Webseite, welche ich als Privatprojekt am entwickeln 
bin, brauche ich einen Server, welcher das HTTP und das Websocket 
Protokoll unterstützt. Aufgrund grösserer Flexibilität habe ich mich 
entschieden einen eigenen WebsocketServer in C++ zu programmieren. 
Dieser funktioniert auch bereits teilweise, aber leider kommt es beim 
aufrufen einer Seite manchmal zu einem Segmentation fault. Ich konnte 
leider noch nicht herausfinden, was zu diesem Segmentation fault führt, 
und hoffe hier auf Tipps, Hinweise, Kritik und/oder 
Verbesserungsvorschläge.

Den Sourcecode gibt es hier:
  http://87.102.188.235/WebsocketServer.zip

GDB hat mir folgendes verraten:
1
Program received signal SIGSEGV, Segmentation fault.
2
[Switching to Thread 0x7ffff6e92700 (LWP 5005)]
3
0x00000000004052d0 in client::ws_send(void*) ()
4
(gdb) backtrace
5
#0  0x00000000004052d0 in client::ws_send(void*) ()
6
#1  0x00000000004059a5 in task::solve(unsigned char*) ()
7
#2  0x0000000000402eca in c_worker::do_work() ()
8
#3  0x0000000000402ff2 in worker() ()
9
#4  0x00000000004026b0 in callThreadFunc(void*) ()
10
#5  0x00007ffff7bc7b50 in start_thread (arg=<optimized out>) at pthread_create.c:304
11
#6  0x00007ffff6f6f0ed in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
12
#7  0x0000000000000000 in ?? ()
Gast #3610705
Lesenswert?
• ▲
▼
Und jetzt erwartest du dass sich jemand gratis den Kopf für dich 
zerbricht und dein Programm auseinander nimmt um deinen Fehler zu 
finden?

Das darfst du schön selber machen. Kompiliere & Linke mit "-g" um 
bessere debug-Informationen zu erhalten. Schaue in GDB nach wo genau der 
Fehler auftritt. Finde heraus was den Fehler verursacht. Verwende 
"valgrind" um Speicherfehler zu finden ( Fehler die nicht sofort zum 
Absturz führen ).
Gast #3610716
Lesenswert?
• ▲
▼
1
void forEachFile(const char* path,void(*func)(const char*,const char*)){
2

3
    DIR *dp = opendir(path);
4
    struct dirent *dirp;
5

6
    if(!dp) {
7
      printf("error: failed to opendir \"%s\"",path);
8
      return;
9
    }
10

11
    char* p = (char*)malloc(1024);
12
    while( (dirp=readdir(dp)) ){
13
      if(!dirp->d_name||dirp->d_name[0]=='.'||!dirp->d_name[0])
14
        continue;
15
      sprintf(p,"%s/%s",path,dirp->d_name);
16
      (*func)(p,m_basename(dirp->d_name));
17
    }
18
    free(p);
19

20
    closedir(dp);
21

22
    return;
23
}
1
  FILE* f = fopen(path,"rb");
2
  if(!f){
3
    printf("error: cann't open file %s\n",path);
4
    redirect(buff,"/");
5
    return false;
6
  }
1
        for(unsigned j=0;j<header_field_list_length;j++){
2
          if(!strcmp(header_field_list_names[j],(char*)&buff[n])){
Das nennst du C++ Code?! Das sieht eher nach "schönstem" C aus.
1
    c_worker** _workers = (c_worker**)realloc(workers,(i+1)*sizeof(c_worker*));
Was ist das?! Schonmal von std::vector gehört?
1
  new server("0.0.0.0",80);
Speicher aufräumen, braucht ja keiner?!

etc. etc. ...
Gast #3610723
Lesenswert?
• ▲
▼
der code ist furchtbar!

Wenig C++ - viel C - viele mögliche fehler!

>  str = (char*)realloc(str,s);
>  str[s-1]=0;

was ist wenn kein Speicher da ist?

> #include "c_worker.cpp"
wer macht denn so etwas?

auch sollte man wenn man schon C++ macht eine string lib benutzen. Dann 
könnte man sich soetwas unlesbares sparen:

> for(a=str+s-1;a>=str  &&(*a==' '||*a==0);a--);

Da ist überhaupt keine sinnvolle Struktur erkennbar.

Warum wird das ganze http Protokoll in Client.cpp zerlegt? Dafür 
schreibt man sich eine http Klasse.

Auch für die Socket sollte man an anfang gleich eine Klasse oder eine 
fertige C++ lib nutzten. So verliert man doch alle Vorteile der 
Fehlerbehandlung von C++

die http Header sind ideal für eine std::map - aber hier wird wieder 
manuell mit listen gearbeitet.


Mit diesen Code würde ich keine Webserver laufen lassen, hier ist es nur 
eine Frage der Zeit bis jemand von außen mit einen Buffer Overflow das 
System lahmlegt oder gleich übernimmt.

Ein fehler wird hier nicht so schnell jemand finden.
Gast #3610755
Lesenswert?
• ▲
▼
Dann hoffen wir mal, dass auch in zukünftigen Versionen niemals später 
Worker herausgenommen werden. Denn dann musst du dich ja daran erinnern 
(mit Post-It am Monitor zB) den Speicher dann doch freizugeben.
Alternativ kann man natürlich auch sauberen Code schreiben der auch 
später noch wiederverwendet/modifiziert werden kann:
1
#include <memory>
2

3
static constexpr BUFF_MAX = 1024; // #define ist gefährlich und unnötig, besser Konstanten verwenden
4
class c_worker {
5
  std::unique_ptr<unsigned char[]> buff;
6
...
7
c_worker::c_worker () {
8
  buff = std::unique_ptr<unsigned char[]> (new unsigned char [BUFF_MAX]);
9
}
Und schon wird der "buff" automatisch gelöscht und du sparst dir das 
Post-It.
sizeof(unsigned char) ist übrigens immer 1.
Gast #3610870
Lesenswert?
• ▲
▼
> Das ist kein leak, weil:

Doch, ist ein klassischer Leak.

> a) c_worker::buff nur einmal, bei der Initialisierung speicher reserviert

..jede Instanz von c_worker

> b) Immer benötigt wird

..maximal so lange wie die jeweilige Instanz von c_worker lebt.

> c) Das Programm nie beendet wird

Sowas gibts per se nicht.

Glaubs mir. Räum auf, sonst findest Du die anderen Leaks nie.
Persönliche Seite #3610898
Lesenswert?
• ▲
▼
Core dump erstellen lassen und das post-mortem Dump analysieren, z.B. 
mit Debugger.

Je nachdem, was für ein Fehler es ist, kann es kontraproduktiv sein, auf 
Verdacht die Quelle an X Stellen zu ändern.  Wenn die Fehlstelle nicht 
dabei ist (wahrscheinlich) und die Änderungen den Bug lediglich 
maskieren, grüßt spätestens in ein paar Wochen das Murmeltier...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren