sscanf auf nicht-nullterminierten String anwenden



  • Hört sich erst mal bescheuert an, aber lasst mich erklären.

    Ich bekomme über HTTP Daten rein, die auch mal binär sein können. Sprich, Null-Bytes ( '\0' ) beinhalten. Oder auch nicht. Das ist immer so Glücksache.
    Die Daten sind in Tupeln zusammengefasst, ein Tupel in je einem Block, und davon mehrere in einem Response. Weil ich keinen Bock hatte, extra dafür einen Parser zu schreiben, habe ich eine Funktion geschrieben, welches an den Anfang jedes Tupel springt, die Position dann sscanf übergibt, und wenn am Ende nicht die Anzahl der Variablen, die gesetzt wurden, rauskommt, werfe ich das Tupel weg. Weil das ganze eh erst mal nur auf Linux läuft, habe ich dann direkt %m verwendet (reserviert mir bei Strings direkt ausreichend Speicher). Keine schöne Lösung, weil ich dann jedes Mal zig malloc -Calls habe und ich (zumindest bisher) sscanf auch bisher noch nicht einen meiner Allokatoren unterschieben konnte. Aber es funktioniert. So einigermaßen.

    Jetzt bin ich aber über ein echtes Problem gestolpert: weil ich in letzter Zeit vom Code-schreiben in Code-auditieren abrutsche, habe ich valgrind auf das Programm angewendet, und das wirft mir direkt für das sscanf eine Warnung raus:

    ==5924== 204 errors in context 5 of 5:
    ==5924== Thread 2:
    ==5924== Invalid read of size 1
    ==5924==    at 0x4C2F114: rawmemchr (vg_replace_strmem.c:1240)
    ==5924==    by 0x55DE72F: _IO_str_init_static_internal (in /lib64/libc-2.19.so)
    ==5924==    by 0x55CDF6F: __isoc99_vsscanf (in /lib64/libc-2.19.so)
    ==5924==    by 0x55CDEFE: __isoc99_sscanf (in /lib64/libc-2.19.so)
    ==5924==    by 0x407378: process_thread (main.c:1112)
    ==5924==    by 0x534F239: start_thread (in /lib64/libpthread-2.19.so)
    ==5924==    by 0x564E5FC: clone (in /lib64/libc-2.19.so)
    

    Einen Beweis dafür, dass das daran liegt, dass die an sscanf durchgereichten Daten einen Fehler verursachen, weil das Nullterminierungszeichen fehlt, habe ich nicht. Aber denken kann ich es mir schon, wenn man bedenkt, dass strncpy auch so beim Nullterminerungszeichen aufhört ...

    Meine Frage also: gibt es irgendeine Möglichkeit, sscanf zu sagen, dass die Daten, die es von mir bekommt, nur eine bestimmte Länge haben (die zu übergeben ist kein Problem)? Eine Möglichkeit wäre, einfach nochmal Speicher +1 zu reservieren und dann am Ende einfach ein '\0' reinzupacken, aber das ist blöd, weil damit das Problem nicht gelöst ist, wenn dann auch Nullbytes Teil des Tupel ist. Oder direkt sscanf wegwerfen und damit auch den Rattenschwanz der malloc -Calls (weil ich mit meinem eigenen Parser dann direkt die Länge bestimmen kann und dann notfalls auch einfach auf dem Stack oder mit meinem Allokator reservieren kann)?


  • Mod

    Du hast die beiden Möglichkeiten schon selber aufgezählt:
    1. Wenn du sscanf benutzen möchtest: sscanf steht für string scanf und laut Konvention der C-Standardbibliothek müssen strings/Zeichenketten mit '\0' abgeschlossen werden. Außerdem gilt entsprechend auch umgekehrt, dass sscanf nicht mit einer Zeichenkette zurecht kommen wird, die in der Mitte Nullzeichen enthält, eben weil sscanf dies als Ende der Zeichenkette wahr nimmt.
    2. Oder du musst eben Funktionen benutzen oder selber schreiben, die nicht die C-Konvention für Zeichenketten erwarten.

    Oder es gibt natürlich immer noch die Meta-Möglichkeit: Keines davon, stattdessen über dein eigentliches Problem nachdenken:

    Ich bekomme über HTTP Daten rein, die auch mal binär sein können. Sprich, Null-Bytes ('\0') beinhalten. Oder auch nicht. Das ist immer so Glücksache.
    Die Daten sind in Tupeln zusammengefasst, ein Tupel in je einem Block, und davon mehrere in einem Response.

    Erzähl mal mehr!



  • SeppJ schrieb:

    Du hast die beiden Möglichkeiten schon selber aufgezählt:

    Großartig. :p
    Ich hatte gehofft, mir genau das zu ersparen ...

    SeppJ schrieb:

    Erzähl mal mehr!

    Ach, SeppJ, was soll es da groß zu erzählen geben? Integerwerte, die reinkommen, sind halt Big-Endian kodiert, aber wenn ein Integerwert == 0 ist, dann ist das eigentlich gleichbedeutend mit Stringende - zumindest, wenn es sich um Funktionen handelt, die auf so was achten. Es kommen halt sowohl "normale" alphanumerische Strings vor (in Anführungszeichen, nicht durch '\0' terminiert) wie auch die Integerwerte, die direkt binär gesendet werden. So viel gibt es da einfach nich zu erzählen - Binär und Text werden zusammen im gleichen Protokoll versendet. HTTP macht ja auch keine Vorschriften darüber, WAS damit übertragen wird.

    Was das für ein Protokoll ist, will ich jetzt lieber nicht sagen. Dass ich damit meine Aussicht auf Hilfe beschneide, ist mir klar - aber ich sehe es eh kommen, dass ich dann doch einen Parser schreiben darf.


  • Mod

    Aber das Protokoll muss doch entscheiden können, welche Art von Daten da kommen. Und Binärdaten kannst du eben nicht mit sscanf verarbeiten. Ich sehe gerade auch nicht, wieso man das würde wollen. Wenn die Daten binär vorliegen, ist eine formatierte Lesefunktion doch völlig ungeeignet. scanf & Co. sind zum Verarbeiten von menschenlesbaren Datenströmen gemacht.

    ich sehe es eh kommen, dass ich dann doch einen Parser schreiben darf.

    Wenn das ein eigenes Protokoll ist und es kein automatischer Parsergenerator sein soll, dann bleibt eben nur neu schreiben.


Log in to reply