UB Falle mit Compound Literal

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 590
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Wohnort: Zürich
Kontaktdaten:

UB Falle mit Compound Literal

Beitrag von starcow »

Ich bin heute wohl fast in eine (wie ich finde, richtig fiese) UB Falle gelaufen.
Jedenfalls scheint es tatsächlich UB zu sein, soweit ich es bis jetzt recherchieren konnte.

Grundidee: Wird als Funktions-Argument ein Null-Pointer übergeben, soll dieser auf einen gültigen Stack-Bereich gebogen werden.
Somit kann nach einmaligem Prüfen normal mit dem Pointer gearbeitet werden.
Da dieser temporäre Stack-Bereich sowieso nur über diesen Pointer erreichbar sein soll, würde sich ein Compound Literal geradezu anbieten (abgesehen von gnu C++ extensions nur in C).

Code: Alles auswählen

if(!p_opt_output)
    p_opt_output = &(struct foo){ 0 };
Soweit klar: Ein Compound Literal, das innerhalb einer Funktion steht, hat automatic storage duration und Block Scope.
Also alles gut - genauso, wie man es haben will.

Leider nein, denn ein if statement bildet immer einen eigenen Block und damit einen eigenen Block Skope, dessen Ende direkt hinter dem if-statement liegt. Unabhängig davon, ob {} verwendet wird.
Das Compound Literal wird also nach dem if statement ungültig und der Zugriff per Pointer UB.
Mit

Code: Alles auswählen

if(!p_opt_output)
{
    p_opt_output = &(struct foo){ 0 };
}
wäre es gleichermassen UB - doch würde man hier wohl eher auf den Trichter kommen.

Soweit ich das bis jetzt recherchieren konnte, ist das mittels Ternary Operator tatsächlich anders und wohldefiniert.

Code: Alles auswählen

p_opt_output = p_opt_output ? p_opt_output : &(struct foo){ 0 };
Edit:
Ich habe die Beispiele etwas angepasst. Mit einem pointer to struct ist es wohl näher an der Realität.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8401
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: UB Falle mit Compound Literal

Beitrag von Krishty »

Nette Falle; die versteckt sich auch in einigen Beispielen der Microsoft-Docs …

Wie wurdest du auf den Fehler aufmerksam? Valgrind? Address Sanitizer?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 590
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Wohnort: Zürich
Kontaktdaten:

Re: UB Falle mit Compound Literal

Beitrag von starcow »

Krishty hat geschrieben: 18.01.2026, 20:42 Wie wurdest du auf den Fehler aufmerksam? Valgrind? Address Sanitizer?
Manchmal lasse ich die KI über kleine Code-Snippets von mir drüber: Einfach um sicher zu sein, dass ich keine Randfälle übersehen habe - und weil es manchmal ganz aufschlussreich sein kann, was die KI in diesem Kontext so alles an Stichworten aufführt.
(Macht ihr das auch so? Das hat mir schon echten Nutzen gebracht)

Die KI hatte zwar den Bug selbst nicht erkannt, doch da sie das Stichwort "Block Scope" aufgeführt hatte, dämmerte es mir.
Ich hatte vor einiger Zeit mal genauer untersucht, was der Standard zu if-Anweisungen sagt, die nicht von einem Anweisungsblock umschlossen sind - also nur ein einzelnes statement als body aufweisen (Antwort: Es macht keinen Unterschied).

Interessant finde ich an dieser Stelle auch, dass mir gcc folgendes Beispiel nicht compiliert, währen es clang mit einer "unused variable" Warnung durchwinkt.

Code: Alles auswählen

if(1) int x = 3;
gcc: error: expected expression before 'int'
clang: warning: unused variable 'x' [-Wunused-variable]


Krishty hat geschrieben: 18.01.2026, 20:42 Nette Falle; die versteckt sich auch in einigen Beispielen der Microsoft-Docs …
Wie schlimm ist das jetzt? Sollte man das melden?
Freelancer 3D- und 2D-Grafik
mischaschaub.com
NytroX
Establishment
Beiträge: 425
Registriert: 03.10.2003, 12:47

Re: UB Falle mit Compound Literal

Beitrag von NytroX »

Ich finde das passiert halt auch schnell wenn man versucht den Code zu stark zu "optimieren", also viel Bedeutung in eine Zeile zu packen.
In anderen Sprachen ok, in C oder C++ oft schwierig.

Ich schreibe immer ordentlich die Klammern beim "if" hin.
Auch mit den Compound Literals gehe ich eher sparsam um, besonders wenn man gleich einen Pointer draus macht.
Einfach aus der Vergangenheit weil R-Values ja z.T. keine Adresse haben, die Schreibweise mit dem Compound Literal ist ja ein Sonderfall.

Code: Alles auswählen

auto *result = &make_struct(); // ups...
Einfach alles sauber ausschreiben:

Code: Alles auswählen

struct foo s{0};
if(!p_opt_output) {
  p_opt_output = &s;
}
Wo dann auch gleich die Alarmglocken leuchten:
Ich mache ein lokales Struct, der Pointer heißt aber irgendwas mit "output"... da sehe ich ein potentielles Problem ohne den Rest der Funktion zu kennen.
Benutzeravatar
dot
Establishment
Beiträge: 1752
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: UB Falle mit Compound Literal

Beitrag von dot »

starcow hat geschrieben: 19.01.2026, 19:00 Interessant finde ich an dieser Stelle auch, dass mir gcc folgendes Beispiel nicht compiliert, währen es clang mit einer "unused variable" Warnung durchwinkt.

Code: Alles auswählen

if(1) int x = 3;
gcc: error: expected expression before 'int'
clang: warning: unused variable 'x' [-Wunused-variable]
Scheint ein Bug zu sein der in clang 19.x behoben wurde…
Krishty hat geschrieben: 18.01.2026, 20:42 Nette Falle; die versteckt sich auch in einigen Beispielen der Microsoft-Docs …
Die Microsoft-Docs verwenden compound literals?
Benutzeravatar
Krishty
Establishment
Beiträge: 8401
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: UB Falle mit Compound Literal

Beitrag von Krishty »

dot hat geschrieben: Gestern, 16:56 Die Microsoft-Docs verwenden compound literals?
Nein, aber sie haben Variablen in if-Blöcken und speichern Zeiger drauf, die sie außerhalb der Blöcke benutzen.

Fällt wohl auch deshalb nie auf, weil MSVC immer Stack für *alle* Variablen reserviert, unabhängig davon, in welchem Scope sie liegen.

War IIRC in den Beispielen für IThumbnailHandler oder so.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1752
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: UB Falle mit Compound Literal

Beitrag von dot »

Krishty hat geschrieben: Gestern, 17:04
dot hat geschrieben: Gestern, 16:56 Die Microsoft-Docs verwenden compound literals?
Nein, aber sie haben Variablen in if-Blöcken und speichern Zeiger drauf, die sie außerhalb der Blöcke benutzen.
Ah, alles klar ^^
Antworten