• Diese Seite verwendet Cookies. Indem du diese Website weiterhin nutzt, erklärst du dich mit der Verwendung von Cookies einverstanden. Erfahre mehr

Kleines unlogisches Problem mit C (OpenBSD + clang)

Themenstarter #1
(kann gerne in Geplauder verschoben werden, war mir nicht ganz sicher wo es besser aufgehoben ist)

Hallo,
habe neulich ein Meme betrachtet mit dem Inhalt
const MY_MIND = true
Darunter mit der Zeile:
Change MY_MIND

Hier ein Link zum Bild
https://img.pr0gramm.com/2018/08/08/5b489a85732c2bc1.jpg

Natürlich musste ich das mal ausprobieren, was in 90 % der Fälle auch super funktioniert hat, jedoch nicht unter OpenBSD:
Code:
#include <stdio.h>
#include <stdlib.h>


int
main()
{
        const int MY_MIND = 1;
        (void) printf("%p:\t%i\n", &MY_MIND, MY_MIND);

        char *ptr;
        ptr = (char *) &MY_MIND;

        int i;
        for (i = 0; i != sizeof(MY_MIND); i++) {
            ptr[i] = '\0';

            if (ptr[i] == '\0')
                (void) printf("%p:\tnull\n", &ptr[i]);
        }
        (void) printf("%p:\t%i\n", &MY_MIND, MY_MIND);

        for (i = 0; i != sizeof(MY_MIND); i++) {
            if (ptr[i] == '\0')
                (void) printf("%p:\tnull\n", &ptr[i]);
        }

        return EXIT_SUCCESS;
}
Ausgabe unter Linux (gcc):
Code:
0xffffcbec:     1
0xffffcbec:     null
0xffffcbed:     null
0xffffcbee:     null
0xffffcbef:     null
0xffffcbec:     0
0xffffcbec:     null
0xffffcbed:     null
0xffffcbee:     null
0xffffcbef:     null
Hier wurde const int MY_MIND wie gewünscht geändert.

Ausgabe OpenBSD (clang):
Code:
0x7f7ffffe004c: 1
0x7f7ffffe004c: null
0x7f7ffffe004d: null
0x7f7ffffe004e: null
0x7f7ffffe004f: null
0x7f7ffffe004c: 1
0x7f7ffffe004c: null
0x7f7ffffe004d: null
0x7f7ffffe004e: null
0x7f7ffffe004f: null
Wo wird die 1 eingetragen, nach der Ausgabe müsste der gesamte Bereich mit '\0' überschrieben sein? Gibt es eine kurze Möglichkeit wie ichs unter OpenBSD umgehe?

Um die erste Frage gleich zu Umgehen: "Weil ich Lust darauf habe" :)
 
Themenstarter #2
Ach, kaum mach ich den Thread auf, schon fällt mir ein Weg ein:
Code:
(void) printf("%p:\t%i\n", &MY_MIND, *(&MY_MIND));
Werden const automatisch anders abgelegt?
 

schorsch_76

FreeBSD Fanboy
#3
Das ist ein Problem mit den C Style Cast. Siehe hierzu

https://stackoverflow.com/questions...rence-between-static-cast-and-c-style-casting

Das Problem ist vermutlich, dass der Clang hier den const weg optimiert und immer 1 schreibt. Der gcc macht eine Variable die dann über den Pointer geändert wird. Ich sehe das als undefined Behaviour an oder im Bestfall als Compiler spezifisches Verhalten. Es ist auf jeden Fall ein Problem das man in einem normalen Programm auf jeden fall vermeiden muss.

const sollte auch nicht änderbar sein. Höchstens es ist mutable.
https://en.cppreference.com/w/cpp/language/cv
 
Themenstarter #4
Ich sehe das als undefined Behaviour an oder im Bestfall als Compiler spezifisches Verhalten.
Laut deinem 2. Link ist indirektes Ändern einer const Variable (über Pointer oder ähnliches) undefinded behavior

Das Problem ist vermutlich, dass der Clang hier den const weg optimiert und immer 1 schreibt.
Hiermit kann ich gut leben, dachte schon mein Weltbild muss sich wieder ein wenig drehen

Clang ist schon irgendwie eigenartig, wenn man mit gcc / tc gelernt hat, naja ist umgekehrt sicher ähnlich.
 

Tron

Well-Known Member
#5
Clang ist schon irgendwie eigenartig, wenn man mit gcc / tc gelernt hat, naja ist umgekehrt sicher ähnlich.
Das hat nichts speziell mit clang zu tun.
Dein Programm hat laut der C-Norm ab der ersten Ausführung von ptr[i] = '\0'; keine Bedeutung mehr -- undefined behavior, wie du korrekt festgestellt hast.
Es ist somit falsch, irgendein bestimmtes Verhalten zu erwarten.
 

schorsch_76

FreeBSD Fanboy
#7
Ich weiß was du meinst, diese Taktik von Clang kann aber recht schnell einen Pufferüberlauf verdecken, der den einen Wert anzeigt, aber in Funktionen bei der Übergabe mit einem anderen rechnet.
Das ist nicht das Problem von Clang. Dein Code ist nicht standardkonform und du erwartest aber definiertes Verhalten. Du erwartest also "standardisiertes undefiniertes Verhalten"....
 

Tron

Well-Known Member
#9
Code:
static int const v = 0;
int main(void)
{
    *(int*)&v = 1; /* FALSCH: Verhalten ist undefiniert! */
    return v;
}
Nur ein kleines Gegenbeispiel, dass gcc es "richtig" machen würde:
Hier liefert gcc 7.3.0 (wahrscheinlich auch andere Versionen) 0 als Rückgabewert von main().

Es wäre auch völlig korrekt, wenn der Übersetzer statt der Zuweisung einfach einen Aufruf an abort() einfügen würde.
Das wäre sogar recht nett, aber dazu müsste der Übersetzer erstmal selbst rausfinden, dass hier undefiniertes Verhalten vorliegt, was mehr Aufwand beim Übersetzen bedeuten würde.
Dieser Aspekt ist auch ein weit verbreiteter Irrtum:
Seltsames Verhalten bei undefiniertem Verhalten entsteht üblicherweise nicht, weil die Übersetzerbauer böswillig sind und bei undefiniertem Verhalten den Übersetzer einfach Schlimme Dinge(TM) tun lassen.
Sondern es wurde eben nicht festgestellt, ob undefniertes Verhalten vorliegt, sondern es wird (völlig korrekt aus Sicht der Sprachnorm) schlicht davon ausgegangen, dass eben kein undefiniertes Verhalten passiert und unter dieser Annahme wird das Programm übersetzt.

Nochmal in aller Deutlichkeit:
Irgendetwas bestimmtes bei undefiniertem Verhalten zu erwarten ist grundfalsch.
Einen Pufferüberlauf musst du verhindern, bevor er passiert, z.B. durch Prüfen, ob der Reihungsindex klein genug ist.
Im Nachhinein kannst nur nur eine zufällig entstandene Leiche im gdb sezieren.