Forum: PC Hard- und Software **stack smashing detected** nach Aufbau einer TCP Verbindung


von etechniker (Gast)


Lesenswert?

Hallo zusammen

Auf einem Linux Rechner müsste ich eine TCP Verbindung mit einem uC 
Board aufbauen. Dabei erhalte ich eine "stack smashing detected" 
Fehlermeldung.

Der Code zum Erstellen des sockets ist folgender:
1
//tcp variables
2
int sock_cli;                       //socket fd
3
struct sockaddr_in servaddr;        //address struct
4
#define TNB_MNS_PORT  30
5
6
bool init_comm(void){
7
    sock_cli = socket(AF_INET,SOCK_STREAM, 0);
8
    memset(&servaddr, 0, sizeof(servaddr));
9
    servaddr.sin_family = AF_INET;
10
    servaddr.sin_port = htons(TNB_MNS_PORT);
11
    servaddr.sin_addr.s_addr = inet_addr("192.168.0.4");
12
13
    //try to establish a tcp connection to the ccard
14
    ROS_INFO("Establishing TCP connection");
15
    if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
16
        return false;
17
    ROS_INFO("TCP connection established");
18
}

Die Verbindung wird in der main() Routine erzeugt:
1
int main(int argc,char** argv){
2
    ros::init(argc, argv, "acgen_node");
3
4
    ROS_INFO("startup");
5
6
    ros::NodeHandle nh("~");
7
8
    //init ttycomm
9
    bool comm_est_succeeded=init_comm();
10
    if(!comm_est_succeeded){
11
        ROS_ERROR("Could not establish TCP connection to ControlCard, aborting.");
12
        ros::shutdown();
13
        return 0;
14
    }
15
    ROS_INFO("Established Connection");
16
17
    ros::spin();
18
}

Der Output dieses Programms ist der Folgende:
1
[ INFO] [1639402661.886493096]: startup
2
[ INFO] [1639402661.889565903]: Establishing TCP connection
3
[ INFO] [1639402661.890875668]: TCP connection established
4
*** stack smashing detected ***: terminated
5
Aborted (core dumped)

Soweit ich das beurteilen kann, scheint der entsprechende Fehler direkt 
nach dem Aufbau der TCP connection zu erfolgen, sodass die dritte 
Konsolenausgabe (ROS_INFO("Established Connection");) nicht mehr 
erfolgt.

Ich weiss im Moment leider nicht weiter, das Problem scheint von einer 
Array-Überschreibung zu kommen?

von cdector (Gast)


Lesenswert?

Deine Funktion init_comm hat keinen default return Wert.

Das wird bestimmt Probleme verursachen. Eigentlich müsste der compiler 
eine Warnung ausgeben. ggf. mal auch nach anderen Warnungen gucken.

von etechniker (Gast)


Lesenswert?

cdector schrieb:
> Deine Funktion init_comm hat keinen default return Wert.
>
> Das wird bestimmt Probleme verursachen. Eigentlich müsste der compiler
> eine Warnung ausgeben. ggf. mal auch nach anderen Warnungen gucken.

Ja, das war das Problem, mit einem `return true` funktioniert alles.

Ich habe die Compiler-Warnung übersehen.

Vielen Dank!

von Programmierer (Gast)


Lesenswert?

Das Programm in valgrind ausführen hilft in solchen Fällen ungemein. Und 
cdector hat natürlich Recht.

von cdector (Gast)


Lesenswert?

Ein paar Anmerkungen noch zu deinem Code:

1. Verwende keine globalen Variablen. Du kannst servaddr in die Funktion 
init_comm packen, da diese danach nicht mehr benötigt wird. sock_cli 
könntest du vielleicht via Pointer als Parameter an die Funktion 
übergeben.

2. Vermeide den bool Datentyp und verwende stattdessen int für den 
return code deiner Funktionen. Damit bleibst du konsistent mit der main 
Funktion.

3. Im Fehlerfall sollte die main Funktion mit EXIT_FAILURE zurück 
kehren, im Erfolgsfall mit EXIT_SUCCESS (fehlt bei dir!)

4. Beim Arbeiten mit sockets sollte man immer den return code überprüfen 
und eine entsprechende Fehlermeldung ausgeben (siehe strerror), da es 
viele verschiedene Ursachen haben kann, warum eine Verbindung nicht 
zustande kommt (no route, timeout, reject etc.).

von Programmierer (Gast)


Lesenswert?

cdector schrieb:
> 2. Vermeide den bool Datentyp und verwende stattdessen int für den
> return code deiner Funktionen. Damit bleibst du konsistent mit der main
> Funktion.

Dass die main-Funktion "int" zurückgibt ist eher ein Kuriosum. "bool" 
als Rückgabewert für Erfolg/Fehler ist absolut sinnvoll und eingängig 
verständlich. Noch besser sind aber Exceptions - wobei es sich hier ja 
um einen erwartbaren Fehler handelt, weshalb der Rückgabewert doch 
angebrachter ist.

von cdector (Gast)


Lesenswert?

Programmierer schrieb:
> cdector schrieb:
>> 2. Vermeide den bool Datentyp und verwende stattdessen int für den
>> return code deiner Funktionen. Damit bleibst du konsistent mit der main
>> Funktion.
>
> Dass die main-Funktion "int" zurückgibt ist eher ein Kuriosum. "bool"
> als Rückgabewert für Erfolg/Fehler ist absolut sinnvoll und eingängig
> verständlich. Noch besser sind aber Exceptions - wobei es sich hier ja
> um einen erwartbaren Fehler handelt, weshalb der Rückgabewert doch
> angebrachter ist.

Das Problem mit bool ist aber, dass du auf zwei Werte beschränkt bist. 
Viele Methoden der Standard-Bibliotheken(libc, POSIX etc.) geben im 
Erfolgsfall auch eine positive Zahl zurück (open, read, write, poll 
usw.). bool kommt quasi nicht vor und wirkt für plain C irgendwie 
fremdartig.

Exceptions sind nochmal eine andere Hausnummer.

von Programmierer (Gast)


Lesenswert?

cdector schrieb:
> Viele Methoden der Standard-Bibliotheken(libc, POSIX etc.) geben im
> Erfolgsfall auch eine positive Zahl zurück (open, read, write, poll
> usw.).

Da könnte man std::optional verwenden.

cdector schrieb:
> bool kommt quasi nicht vor und wirkt für plain C irgendwie
> fremdartig.

Es ist aber C++, und die Standard-Library-Funktionen nutzen 
bool-Rückgabewerte für Erfolg/Fehler.

von Rolf M. (rmagnus)


Lesenswert?

cdector schrieb:
> Das Problem mit bool ist aber, dass du auf zwei Werte beschränkt bist.

Ja. Oft will man nicht nur zurückgeben, dass ein Fehler aufgetreten 
ist, sondern auch, welcher, auch wenn das in heutiger Software 
inzwischen eher out zu sein scheint. Ich würde dafür weder bool, noch 
int verwenden, sondern mir einen enum definieren.

> Viele Methoden der Standard-Bibliotheken(libc, POSIX etc.) geben im
> Erfolgsfall auch eine positive Zahl zurück (open, read, write, poll
> usw.). bool kommt quasi nicht vor und wirkt für plain C irgendwie
> fremdartig.

bool kam halt auch erst später dazu, als alle die genannten Bibliotheken 
schon lange da waren.

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

Moin,

mich würde mal interessieren, welcher Steinzeit-Compiler
eine Funktion mit Returntype bool akzeptiert und beim Fehlen
des Returnkommandos nicht einen Fehler, sondern nur eine Warnung wirft.

Gruß
Olaf

von mh (Gast)


Lesenswert?

Olaf D. schrieb:
> Moin,
>
> mich würde mal interessieren, welcher Steinzeit-Compiler
> eine Funktion mit Returntype bool akzeptiert und beim Fehlen
> des Returnkommandos nicht einen Fehler, sondern nur eine Warnung wirft.
>
> Gruß
> Olaf

Woher soll der Compiler wissen, dass es ein Fehler ist? Dafür müsste er 
wissen was genau in connect und ROS_INFO passiert.

von Rolf M. (rmagnus)


Lesenswert?

Olaf D. schrieb:
> Moin,
>
> mich würde mal interessieren, welcher Steinzeit-Compiler
> eine Funktion mit Returntype bool akzeptiert und beim Fehlen
> des Returnkommandos nicht einen Fehler, sondern nur eine Warnung wirft.

Alle, die ich kenne. Zumindest mal der aktuelle GCC und das aktuelle 
Visual C++ "werfen" in den Default-Einstellungen nur Warnungen. Siehe 
https://godbolt.org/z/qsjfzxvGM
Im Falle von gcc kann man das mit dem Kommandozeilen-Parameter 
-Werror=return-type auf eine Fehlermeldung umschalten.

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

Nein, muss er nicht.

Nach dem if(connect()) kommt kein return mehr.

    if (connect(sock_cli, (struct sockaddr *)&servaddr, 
sizeof(servaddr)))
        return false;

D. h. es gibt im Fehlerfall keinen Returnwert, was natürlich den Stack 
korrumpiert.

Was der MS Compiler zurückliefert weiß ich nicht, da ich den nicht 
verwende.

Was den gcc betrifft, bzw. guten Code, man sollte immer mit -Wall 
übersetzen.

Gruß
Olaf

von Rolf M. (rmagnus)


Lesenswert?

Olaf D. schrieb:
> Nach dem if(connect()) kommt kein return mehr.
>
>     if (connect(sock_cli, (struct sockaddr *)&servaddr,
> sizeof(servaddr)))
>         return false;
>
> D. h. es gibt im Fehlerfall keinen Returnwert, was natürlich den Stack
> korrumpiert.

Falls die if-Bedingung immer true ist, passiert auch nichts. Der 
Compiler kann das aber nicht einschätzen. Daher nur eine Warnung, kein 
Abbruch mit Fehler.

> Was der MS Compiler zurückliefert weiß ich nicht, da ich den nicht
> verwende.

Ich auch nicht, deshalb habe ich den online-Compiler bemüht.

> Was den gcc betrifft, bzw. guten Code, man sollte immer mit -Wall
> übersetzen.

Das ändert aber nichts daran, dass es eine Warnung und kein Fehler ist. 
Du hast aber noch nicht verraten, was für einen höchstmodernen Compiler 
du einsetzt, der per Default einen Fehler draus macht, da alle, die da 
nur warnen, ja "Steinzeit-Compiler" sind.

von cdector (Gast)


Lesenswert?

Mich wundert vor allem, dass der Compiler Code erzeugt, welcher zum 
einen Buffer overflow protection enthält aber dann den Stack beim 
Verlassen der Funktion in keinem konsistenten Zustand hinterlässt.

Hier müsste es eigentlich zu einem compile error kommen, da es kein 
funktionierender Code ist, wie man im OP gesehen hat.

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

> Falls die if-Bedingung immer true ist, passiert auch nichts. Der
> Compiler kann das aber nicht einschätzen. Daher nur eine Warnung, kein
> Abbruch mit Fehler.

Ein connect Aufruf kann immer schiefgehen. Und da der in einer Library 
ist,
kann der Compiler das auch nicht überprüfen. Und deshalb sollte er einen 
Fehler werfen.

Es sieht so aus, dass der GCC dieses Verhalten auch zeigt.
Ich vermute mal, das es der C Spezifikation entspricht.

Das bestätigt wieder einmal, das man seinen eigenen Code lieber in *.cpp 
Datein packen sollte,
um von der besseren Fehlerprüfung von CPP zu profitieren.

Habe das mal eben ausprobiert. In C wird der Code akzeptiert,
in C++ nicht.

Gruß
Olaf

PS: Nehme also die Bemerkung "Steinzeitcompiler" zurück.

PSS: Manche sind echt empfindlich

von Georg A. (georga)


Lesenswert?

cdector schrieb:
> Mich wundert vor allem, dass der Compiler Code erzeugt, welcher zum
> einen Buffer overflow protection enthält aber dann den Stack beim
> Verlassen der Funktion in keinem konsistenten Zustand hinterlässt.
>
> Hier müsste es eigentlich zu einem compile error kommen, da es kein
> funktionierender Code ist, wie man im OP gesehen hat.

Wundert mich auch, ich bin für ein Mini-Test-ex-und-Hop-Progrämmchen (wo 
einem eigentlich Warnings egal sein könnten) auch schon mal genau über 
diesen Effekt gestolpert. AFAIR gabs nur mit -O0 keinen Absturz.

Klar kann man jetzt über "undefiniertes" Verhalten philosophieren. Aber 
das ist etwas, was ganz schnell passieren kann, aber einem sehr viel 
Zeit kostet zu finden. Jeder irrelevante Mist bekommt eine Warning (ala 
"unused variable") aber etwas, was vom Compilerdesign wohl zwangsläufig 
zu einem Crash führt, wird genauso behandelt? Da hat jemand zuviel 
LR-Parser geschnupft, wenn er das für sinnvoll hält...

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Wieso seid ihr euch so sicher, dass es keine Warnung gibt?

Olaf D. schrieb:
>> Falls die if-Bedingung immer true ist, passiert auch nichts. Der
>> Compiler kann das aber nicht einschätzen. Daher nur eine Warnung, kein
>> Abbruch mit Fehler.
>
> Ein connect Aufruf kann immer schiefgehen. Und da der in einer Library
> ist,
> kann der Compiler das auch nicht überprüfen. Und deshalb sollte er einen
> Fehler werfen.

Der Compiler kann nicht wissen, dass es ein Fehler ist (woher auch), 
deswegen darf er keine Fehler erzeugen, maximal eine Warnung.

Und soweit ich weiß, liefert der gcc da eine Warnung.

Georg A. schrieb:
> ... [Blubber] ...
Hast du mal ganz konkret getestet, was der Compiler in diesem Fall sagt?

von mh (Gast)


Lesenswert?

Nur so zur allgemeinen Info:
1
-Wreturn-type
2
Warn whenever a function is defined with a return type that defaults to int. Also warn about any return statement with no return value in a function whose return type is not void (falling off the end of the function body is considered returning without a value).
3
...
4
This warning is enabled by default in C++ and by -Wall otherwise.

von Rolf M. (rmagnus)


Lesenswert?

Olaf D. schrieb:
>> Falls die if-Bedingung immer true ist, passiert auch nichts. Der
>> Compiler kann das aber nicht einschätzen. Daher nur eine Warnung, kein
>> Abbruch mit Fehler.
>
> Ein connect Aufruf kann immer schiefgehen. Und da der in einer Library
> ist, kann der Compiler das auch nicht überprüfen.

Das sage ich ja. Er kann das nicht überprüfen. Undefiniertes Verhalten 
liegt aber nur vor, wenn das if fehlschlägt. Da er nicht weiß, ob dieser 
Codepfad überhaupt je erreicht wird, meldet er keinen Fehler. Erst zur 
Laufzeit ergibt sich das.

> Und deshalb sollte er einen Fehler werfen.

Meines Erachtens darf er das gar nicht.

> Es sieht so aus, dass der GCC dieses Verhalten auch zeigt.

Nein, es ist nur eine Warnung. Das habe ich doch gezeigt. Nur wenn man 
per Kommandozeilenparameter die Warnung ausdrücklich in einen Fehler 
wandelt (was man mit jeder Warnung machen kann), kommt da auch ein 
Fehler.

> Ich vermute mal, das es der C Spezifikation entspricht.

Ich denke nicht. Mir wäre keine Stelle bekannt, wo die Spezifikation das 
erfordert (oder überhaupt erlaubt).

> Das bestätigt wieder einmal, das man seinen eigenen Code lieber in *.cpp
> Datein packen sollte, um von der besseren Fehlerprüfung von CPP zu
> profitieren.

Warum sollte in dieser Situation das Verhalten anders sein als in C?

> Habe das mal eben ausprobiert. In C wird der Code akzeptiert,
> in C++ nicht.

Bei mir wird er mit gcc in C++ akzeptiert. Genau wie bei dem obigen Link 
auf den Online-Compiler.
1
bool myfunc(int i)
2
{
3
    if (i < 0)
4
        return true;
5
}
6
7
int main()
8
{
9
    if (myfunc(3))
10
        return 0;
11
}
Compiler-Aufruf GCC:
1
g++ boolret.cpp -std=c++17 -pedantic -Wall -Wextra 
2
boolret.cpp: In function ‘bool myfunc(int)’:
3
boolret.cpp:5:1: warning: control reaches end of non-void function [-Wreturn-type]
4
    5 | }
5
      | ^
Comiler-Aufruf clang:
1
boolret.cpp:5:1: warning: non-void function does not return a value in all control paths [-Wreturn-type]
2
}
3
^
4
1 warning generated.

> PS: Nehme also die Bemerkung "Steinzeitcompiler" zurück.
>
> PSS: Manche sind echt empfindlich

Was heißt empfindlich. Mir ist nur kein Compiler bekannt, der da mit 
Fehler abbricht. Und wie gesagt glaube ich nicht mal, dass der 
C++-Standard das überhaupt erlaubt.

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> Was heißt empfindlich. Mir ist nur kein Compiler bekannt, der da mit
> Fehler abbricht. Und wie gesagt glaube ich nicht mal, dass der
> C++-Standard das überhaupt erlaubt.

Nicht erlaubt:
1
If a program contains no violations of the rules in this International Standard, a conforming implemen-
2
tation shall, within its resource limits, accept and correctly execute2 that program.

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
Noch kein Account? Hier anmelden.