>>157355 Ich habe es zum Kompilieren bekommen, aber sage gleich, dass das, was der Meister zum Teil vom Herrn Bernstein übernommen hat und zum Teil dort selbst verzapft hat, absolut unterirdisch ist. Je mehr ich dort reinschaue, desto schlimmer wird es.
Es geht um den
buffer
aus der libowfat (buffer.h):
typedef struct buffer {
char *x; /* actual buffer space */
size_t p; /* current position */
size_t n; /* current size of string in buffer */
size_t a; /* allocated buffer size */
ssize_t (*op)(); /* use read(2) or write(2) */
void* cookie; /* used internally by the to-stralloc buffers, and for buffer chaining */
void (*deinit)(void*); /* called to munmap/free cleanup, with a pointer to the buffer as argument */
int fd; /* passed as first argument to op */
} buffer;
Wie man sieht, kriegt der Buffer eine
op
-Funktion übergeben. Und wie man bereits am Kommentar dort sieht: Das können z.B. die POSIX-Funktionen
read()
oder
write()
sein, oder auch andere Funktionen (wie intern in der libowfat genutzt). Da kommt man direkt zum ersten Problem, da
read()
und
write()
nicht die gleiche Signatur haben:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
Also Unterschiede im cv-Qualifizierer des zweiten Parameters.
Im Internet gibt es Diskussionen darüber, dass es UB sei, weil
void *
keine "unqualified version" von
const void *
sei (das wäre wohl bei
void * const
der Fall), und daher die Funktionsaufrufe inkompatibel seien:
https://old.reddit.com/r/C_Programming/comments/18fbsfg/is_casting_a_function_pointer_to_the_same_type/
Darum geht es hier aber nicht, denn das ist ein Luxusproblem im Vergleich zu den nun folgenden Problemen mit externem Kot und internem Kot.
Erst mal muss man sich an der Stelle oben entscheiden: Nimmt man Signatur 1, Signatur 2, oder einen
void *
. Da auch externer Kot solche Punktionszeiger z.B. an
buffer_init
übergibt, muss man
void *
nehmen, da ansonsten der externe Kot einen Kompilerfehler verursacht, da dieser externe Kot sowohl Signatur 1 als auch Signatur 2 verwenden könnte. Mit
void *
gibt es nur eine Warnung, auch nicht schön, aber was will man machen.
Nun zu den Problemen mit dem libowfat-internen Kot. Da ist extremes Voodoo involviert (das Felix auch im originalen Bernstein-Kot bisher nicht gefunden hat). Denn intern in der libowfat wird die
op
-Funktion auch schon mal mit 4 statt 3 Parametern aufgerufen. Das sieht man in
buffer_feed
,
buffer_flush
,
buffer_put
,
buffer_putflush
. Diese leiten den
op
-Funktionszeiger an
buffer_stubborn
oder
buffer_stubborn_read
weiter, welche den Funktionszeiger mit 4 Parametern aufrufen/missbrauchen.
Felix will gar nicht wissen, was passiert, wenn man einen
buffer
mit
read()
/
write()
als
op
anlegt, und dann eine der obigen Funktionen aufruft, und zwar in einer Calling-Convention, bei dem der Aufgerufene den Stack aufräumen muss (z.B. das standardmäßige
stdcall
auf 32-Bit-Fenster, wo auch alle Parameter über den Stack gehen, vielleicht deswegen auch das "Please do not port my software to Windows!"). Das geht hier auf Linux bzw. x86/x86_64 nur gut, weil der Aufrufer zwar was in
rcx
schreibt, aber
rcx
vom aufgerufenen
read()
/
write
/etc. gemäß Calling-Convention ignoriert wird.
Die Besten der Besten.