Seite 1 von 1

UB Falle mit Compound Literal

Verfasst: 18.01.2026, 17:22
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.

Re: UB Falle mit Compound Literal

Verfasst: 18.01.2026, 20:42
von Krishty
Nette Falle; die versteckt sich auch in einigen Beispielen der Microsoft-Docs …

Wie wurdest du auf den Fehler aufmerksam? Valgrind? Address Sanitizer?

Re: UB Falle mit Compound Literal

Verfasst: 19.01.2026, 19:00
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?

Re: UB Falle mit Compound Literal

Verfasst: 19.01.2026, 20:06
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.

Re: UB Falle mit Compound Literal

Verfasst: 20.01.2026, 16:56
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?

Re: UB Falle mit Compound Literal

Verfasst: 20.01.2026, 17:04
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.

Re: UB Falle mit Compound Literal

Verfasst: 20.01.2026, 18:04
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 ^^