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:
[ 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?
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.
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!
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.).
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.
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.
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.
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.
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
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.
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.
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
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.
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.
> 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
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...
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?
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.
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.
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.
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.