Ich hab mir gestern den Spaß erlaubt, und ein paar ncurses-Applikationen gefuzzt. Fuzz testing ist eine Testmethode, bei der man ein Programm mit zufälligen Eingaben "beschießt", und schaut, wie das Programm reagiert, insbesondere, ob es abstürzt. Besonders im Security-Bereich ist in letzter Zeit Fuzz testing relativ beliebt geworden, vor allem weil es ziemlich "billig" ist, einen derartigen Test aufzusetzen, und die Chance, Bugs und insbesondere Buffer Overflows zu finden, relativ hoch ist (man erinnere sich nur an diesen Fall).
Auf jeden Fall hab ich snownews und raggle, meine zwei (featuremäßig unterlegenen) "Konkurrenten" in Sachen RSS-Feedreader für die Textkonsole, mal mit einem simplen "cat /dev/urandom | snownews" bzw. "cat /dev/urandom | raggle" gefuzzt, und beide sind so richtig schön gestürzt, snownews mit einer NULL-Pointer-Dereferenzierung, und raggle mit einem Methodenaufruf auf ein nil-Objekt (raggle ist ja in Ruby geschrieben; auf jeden Fall ist der Fehler konzeptionell ähnlich zu einer NULL-Pointer-Dereferenzierung). Na, was hab ich gelacht.
Zugegeben, wenn man das gleiche mit newsbeuter macht, schmiert der auch schön ab, aber daran bin nicht ich schuld, sondern ncurses.
Ein paar andere Applikationen hab ich auch so nebenbei getestet, so etwa slrn (der ist ganz cool, ignoriert stdin, und macht sich stattdessen /dev/tty auf), mutt (der interpretiert Daten auf stdin, wenn stdin kein TTY ist, als Input für ein Mail), centericq (der interpretiert den Datenmüll als normale Eingabe, stürzt aber nicht ab) und vim (der warnt, dass stdin kein TTY ist, läuft aber auch durch).
Freitag und Samstag war ich auf den Linuxwochen in Wien, und ich muss sagen, das war ein sehr produktives Wochenende. Ich hab selbst einiges an newsbeuter weiterarbeiten können, und Clifford hat auch an der STFL einiges erweitert. So wird jetzt z.B. der Cursor in Listen am Anfang des jeweiligen ausgewählten Eintrag platziert, was inbesondere für Braille-Zeilen relevant ist. Ausserdem können jetzt die Keybindings der Widgets durch eine Belegungen ersetzt oder ergänzt werden (in newsbeuter wird mit diesem Feature die Navigation durch Listen konfigurierbar gemacht), und man kann durch Tastendrücke beliebige Events generieren. Auch ansonsten konnte ich einige Leute von newsbeuter überzeugen, die Userbase wird also noch weiter steigen. Insbesondere die Möglichkeit, etwas direkt herzuzeigen, und sofort auf Fragen eingehen zu können, ist wirklich spannend. Man hat unmittelbares User-Feedback, was als Entwickler äußerst angenehm ist.
Es tut sich wieder was bei newsbeuter. Und zwar hab ich in den letzten Tagen eine Filtersprache entwickelt, um dynamische Abfragen für theoretisch alles zu ermöglichen, primär aber, um Feeds und Artikel in Feeds zu filtern, nach Kriterien wie Autor, Titel, Inhalt, Veröffentlichungsdatum, Anzahl der ungelesenen Artikel, URL des RSS-Feeds, usw. usf. Derzeit ist es so implementiert, dass man in der Feedübersicht die Taste F drückt, eine Abfrage eingibt, die gefilterte Feedübersicht präsentiert kriegt, und diese Filterung wieder löschen kann. Ein Abfrage sieht etwa so aus:
unread_count > 0 and unread_count <= 20
Mit dieser Abfrage erhält man alle Feeds, die mindestens einen, aber 20 oder weniger ungelesene Artikel enthalten. Bald werden auch solche Abfragen möglich sein:
( author =~ "Frank" or author =~ "John" ) and ( title =~ "Linux" or title =~ "FreeBSD" )
Das würde alle Artikel der Autoren "Frank" oder "John" (als Text innerhalb der Quotes sind übrigens Regular Expressions möglich) mit "Linux" oder "FreeBSD" im Titel. Eine freie Gestaltung der Abfragen ist möglich, "the sky is the limit" wie es im Englischen so schön heisst. Die Abfrage
rssurl =~ "^https:"
filtert übrigens alle Feeds mit HTTPS-URL heraus. Wie man merkt, kommt man mit diesen Mitteln schnell zu vorher ungeahnten Möglichkeiten.
Implementiert habe ich den Parser für diese Abfragesprache übrigens mit Coco/R für C++, ein Compilergenerator, der an der Uni Linz entwickelt wird. Das ging ziemlich schnell und intuitiv, und mit den verfügbaren Beispielen für z.B. dem Compiler für die Minisprache Taste war es auch mir Compilerbau-Unerfahrenen möglich, schnell zu einem funktionierenden Parser zu gelangen. Die Code-Templates für die Generierung der Klassen rundherum (die "frame files") musste ich etwas adaptieren, etwa um aus einem std::istream statt aus einem FILE* zu parsen (was den Vorteil hat, über std::ifstream Dateien und über std::istringstream auch Strings anbinden zu können). Die Anbindung an newsbeuter selbst habe ich so gestaltet, dass ich einen Matcher implementiert habe, der einen geparsten Ausdruck gegen jedes beliebige Objekt prüfen kann, solange dieses nur von der Basisklasse matchable (enthält lediglich zwei pure virtual functions zum überprüfen, ob ein Attribut vorhanden ist, bzw. um dessen Wert abzurufen) abgeleitet ist. Damit erhalte ich die größtmögliche Flexibilität, und kann eben theoretisch jedes Objekt matchen (auch wenn das nicht wirklich bei allen Objekten innerhalb von newsbeuter Sinn macht). Die eigentliche Integration war mit diesen Vorarbeiten dann ein Kinderspiel, und mittlerweile ist die Funktion zum Verstecken der bereits gelesenen Feeds sogar über die Filtersprache implementiert (über exakt den gleichen Mechanismus hängen sich dann auch die anderen Filterabfragen rein). Erstmals präsentieren werde ich dieses neue Feature übrigens übermorgen auf den Linuxwochen Wien. Kommt zahlreich.
Endrekursionen sind Rekursionen, bei denen der rekursive Funktionsaufruf die letzte Operation zum Berechnen des Funktionsergebnisses ist. Eine wichtige Eigenschaft von Endrekursionen ist, dass sie zu Iterationen transformiert werden können, d.h. der Speicherverbrauch auf dem Stack bleibt konstant, während bei nicht-endrekursiven Funktionen der Speicherverbrauch auf dem Stack mit der Rekursiontiefe wächst. Das ist insbesondere relevant für funktionale Sprachen, deren einziges Mittel zur Iterationen die Rekursion darstellt. Das sei einmal der Theorieteil.
Gestern hab ich mir aufgrund einer Diskussion mit nion zu dem Thema mal angeschaut, was der gcc mit endrekursiven Funktionen in C denn genau macht. Und siehe da: der gcc transformiert endrekursive C-Funktionen doch tatsächlich zu Iterationen! Folgende Codesnippets zeigen es deutlich:
int sum (int n){
if(n == 0)
return0;
return n + sum (n - 1);
}
wird zu
sum:
pushl %ebp
xorl %eax, %eax
movl %esp, %ebp
movl 8(%ebp), %edx
.L3:
testl %edx, %edx
je .L4
addl %edx, %eax
decl %edx
jmp .L3
.L4:
popl %ebp
ret
Damit das auch etwas praxisrelevanter wird, hier der Code zum rekursiven Freigeben einer verketteten Liste:
void delete_word_r(word_t * e){
if(e){
word_t* next = e->next;
if(e->word)
free(e->word);
free(e);
delete_word_r(next);
}
}
Der gcc macht folgendes daraus:
delete_word_r:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %ebx
movl 8(%ebp), %ebx
.L33:
testl %ebx, %ebx
je .L38
movl (%ebx), %eax
movl 4(%ebx), %esi
testl %eax, %eax
je .L36
subl $12, %esp
pushl %eax
call free
addl $16, %esp
.L36:
subl $12, %esp
pushl %ebx
movl %esi, %ebx
call free
addl $16, %esp
jmp .L33
.L38:
leal -8(%ebp), %esp
popl %ebx
popl %esi
popl %ebp
ret
Coole Sache. In beiden Assembler-Snippets ist weder ein "call sum" noch ein "call delete_word_r" zu finden.
nion hat einen sehr interessanten Artikel zum Thema Callgraphen generieren veröffentlicht. Darin stellt er zwei verschiedene Methoden vor, um Callgraphen zu generieren, und zwar einerseits ein Tool namens "egypt", das aus der Register transfer language, die GCC per Commandline-Switch ausspucken kann, Files für das Tool "dot" generiert, und andererseits eine Methode, bei der über einen GCC-Commandline-Switch für jeden Funktionsein- und -austritt je ein Funktionsaufruf in den Code eingefügt wird, und über eine per LD_PRELOAD nachgeladene Shared Library zur Laufzeit ein dynamischer Callgraph der tatsächlichen Ausführung aufgezeichnet wird, ebenfalls für dot. Für Programmierer, die sich auch mit der Instrumentierung von Code auseinandersetzen, wird der Artikel auf jeden Fall interessant sein.
Mit C++ funktioniert das ganze grundsätzlich auch, allerdings werden die Graphen bei der Verwendung der C++ Standard Library, insbesondere der über Templates realisierten Klassen, sehr schnell sehr gross. Allein das Starten von newsbeuter bei einem leeren Cache-File spuckt ein ca. 30 MB (!!) großes dot-File aus.
Um 7 Uhr hat der Wecker geklingelt, ich drehe mich im Bett um, und der erste Gedanke, der mir durch den Kopf geht: "Ich habe in der CXyzFile-Klasse ein Memory Leak beim Anlegen von CFooIterator-Objekten". Was dann auch gestimmt hat, und das erste, was ich heute in der Arbeit gefixt habe. Sehr schräg.
Nachdem meine verehrte Leserschaft über meine Berichte über die Entwicklung von newsbeuter das ncurses-UI-Toolkit STFL zumindest vom Namen her kennt, möchte ich ein wenig mehr über die Eigenschaften und Features dieser genialen Library schreiben, da sie mir bisher sehr beim Erstellen von Konsolen-UIs geholfen hat.
Die Basis von STFL ist die Layout-Beschreibungssprache, mit der man die grundsätzliche Ausrichtung und Positionierung sämtlicher Controls inklusive (optional) des Inhalts und der Farbgebung. Diese Beschreibungssprache wird in eine Datenstruktur geparsed, die dann später zur Laufzeit einfach modifiziert und abgefragt werden kann. Ein wichtiges Prinzip des Layouting bei STFL sind dabei Horizontal und Vertical Boxes. Man stelle sich z.B. den gesamten Bildschirm vor, den man zuerst in mehrere Boxen "untereinander" teilt. In so eine Box kann man dann ein Control platzieren, oder eine weitere vbox (macht nicht sehr viel Sinn), oder aber eine hbox, welche die Box wiederum in mehrere Boxen nebeneinander teilt, usw. usf. Zusätzlich kann man jeder Box noch die Information mitgeben, in welche Richtung sie expandieren soll. Mit diesem simplen Konzept hat man ein mächtiges Werkzeug bei der Hand, um brauchbare und komplexe Layouts für Konsolen-UIs zu designen.
Zur Laufzeit wird diese Layoutbeschreibung dann eingelesen (entweder aus einer Zeichenkette oder aus einem externen File, ich persönlich verwende dieses Skript um STFL-Layouts direkt in Programme einzubinden, aber trotzdem die Layouts in schönen externen Files im Sourcetree liegen zu haben), und die resultierende Datenstruktur wird dem Programmierer dann als opakes "stfl_form *" zur Verfügung gestellt. Dieses Handle kann dann verwendet werden, um den aktuellen Inhalt von Controls auszulesen, zu verändern, den aktuellen Fokus zu bestimmen und zu ändern, und auch um die Datenstruktur selbst zu modifizieren (damit ließe sich theoretisch zur Laufzeit das Layout umstellen, auch wenn das in der Praxis i.A. wenig zielführend ist). Und natürlich kann man das Formular starten und kriegt Benutzereingaben weitergereicht, auf die man entsprechend reagieren kann.
Eine derartige Layoutbeschreibung sieht übrigens so aus:
vbox
@style_normal:bg=black,fg=white
@info#style_normal:bg=blue,fg=yellow,attr=bold
label#info
text[head]:"Your feeds"
.expand:h
list[feeds]
style_normal:bg=black,fg=white
style_focus:fg=yellow,bg=blue,attr=bold
.expand:vh
pos_name[feedposname]:
pos[feedpos]:0
label#info
text[help]:"q:Quit ENTER:Open r:Reload R:Reload All A:Mark All Read C:Catchup All"
.expand:h
label
text[msg]:""
.expand:h
Das ist das Layout der Feedlist, wie sie bei newsbeuter beim Programmstart erscheint. Es besteht aus einer vbox, welches von oben nach unten ein Label für die Überschrift, eine Liste mit den Feeds, ein Label mit Keyboard Shortcuts sowie ein Label für Information und Fehler enthält, alles gespickt mit diversen Details wie Farbgebung und der Information für die Labels, dass sie horizontal expandieren sollen, sowie für die Liste, dass sie horizontal und vertikal expandieren soll (und damit den meisten Platz ausfüllt).
Insgesamt ist das Interface von STFL sehr kompakt (14 API-Funktionen). Die Konstruktion eines Programms erfolgt meistens nach dem gleichen Prinzip: Initialisierung - Event Loop - Ressourcenfreigabe. Die Initialisierung ist bei STFL eine ganz simple:
Der erste Schritt ist immer, ein Formular anzulegen, und zwar mit stfl_create(). Dabei gibt es zwei Varianten, und zwar, einen normalen String mitzugeben, dann wird der String als Layoutbeschreibung geparsed, oder aber, man übergibt einen String, der mit '<' anfängt, und mit '>' endet, dann wird der Inhalt der Datei, deren Name zwischen '<' und '>' steht, als Layoutbeschreibung verwendet. Damit kann man auch Layouts aus externen Dateien laden, ohne diese Datei selbst öffnen und auslesen zu müssen, und man muss auch keinen extra API-Call bedienen.
Der nächste Schritt ist dann, das Formular zu starten, und optional auch auf Eingaben des Users zu warten. Und zwar funktioniert das mit stfl_run().
char * event = NULL;
event = stfl_run(f1, 0);
stfl_run(f1, -1);
Der zweite Parameter gibt dabei, wie lange auf Benutzereingabe gewartet werden soll, welche dann zurückgegeben wird. Der Wert "-1" bedeutet, dass keine Benutzereingaben eingelesen werden (wenn man z.B. lediglich das Formular neu zeichnen will), und 0 bedeutet, dass unendlich lange auf Benutzereingabe gewartet wird. Bei einem Wert > 0 wird soviele Sekunden gewartet. Findet bis dahin keine Benutzereingabe statt, so wird von stfl_run() NULL zurückgeliefert. Hierbei sollte man allerdings auf jeden Fall beachten, dass es auch sein kann, dass auch bei einer Wartezeit von 0 NULL als Rückgabewert zurückkommen kann, weil z.B. ein Tastendruck von dem Widget, das den Fokus hat, "konsumiert" wurde.
Die zurückgegebene Eingabe kann folgendes sein:
"TIMEOUT": bei einem Timeout
"ENTER": wenn die Eingabetaste gedrückt wurde
"ESC": wenn die Escape-Taste gedrückt wurde
"F0" .. "F63": wenn eine F-Taste gedrückt wurde
"CHAR(%u)" (wobei %u einem unsigned integer entspricht): eine andere Taste wurde gedrückt, und der numerische Wert dieser Taste befindet sich zwischen den Klammern. Der numerische Werte für die Taste "a" ist z.B. 97. Und auch Ctrl-Tastenkombination sind damit abfragbar, so ist etwa Ctrl-A der numerische Wert 1, Ctrl-B der Wert 2, usw.
Am Ende eines Programmes ist es dann noch zweckmäßig, die Funktion stfl_reset() aufzurufen, um den Bildschirm entsprechend zu resetten. Mit diesem Wissen kann man schon ein nettes kleines Programm schreiben, das so eine typische Event Loop demonstriert, und zusätzlich führe ich auch noch ein paar weitere Funktionen ein:
Was man hier sieht, ist eine klassische Event Loop, wobei auf die Tasten a bis e sowie q reagiert wird. Es werden ja nach Tastendruck ein Label bzw. die Liste modifiziert. Mit der Taste e enthält man zusätzlich noch Informationen über die aktuelle Auswahl in der Liste.
Man kann noch wesentlich mehr mit der STFL machen, und dieser Artikel soll nur einen kleinen Einblick in die grundsätzliche Funktionsweise von STFL bringen, um dem einen oder anderen unter euch das Entwickeln von eigenen, neuen Texttools schmackhaft zu machen. Für mehr Informationen sowie andere Widgets wie Eingabefelder oder mehrzeige Text Views ist es ratsam, in die STFL-Dokumentation sowie die existierenden Beispielprogramme reinzuschauen. Es lohnt sich.
Wer übrigens die STFL "C++iger" verwenden will, den kann ich nur auf meinen "stflpp"-Wrapper hinweisen, den man hier (Header) und hier (Source) findet (MIT/X-Lizenz, BTW). Die STFL selbst unterstützt neben C übrigens auch SPL, Ruby, Python und Perl.
Bald(tm) wird es ein neues Release von newsbeuter geben, und so hab ich einen Screencast gemacht, um zu zeigen, was denn so die wichtigsten Features von newsbeuter sind, und wie man es so benutzt:
Mein Englisch ist relativ schlecht, der Redefluss manchmal unterbrochen, aber wer will schon eine Demo von ueber 10 Minuten ganz exakt skripten, und beim kleinsten Versprecher sofort von neuem anfangen? Ich nicht.
Folgendes Problem: newsbeuter, mein RSS-Feedreader, benützt zum Reloaden aller Feeds einen Thread, der im Hintergrund läuft, und dabei den Fortschritt im mit STFL gezeichneten UI updated. Unter Linux funktioniert das alles perfekt, lediglich unter OSX beobachte ich das absolut schräge Verhalten, dass der Thread nur weiterarbeitet, wenn ich im Vordergrund irgendwelche Sachen mache, wie z.B. mit dem Cursor in der Liste rauf- und runterscrollen, oder zufällige Tasten drücken. Das lässt ja schlimmes vermuten, deswegen die Frage, ob da wer Details kennt. Anyone?
Debian-Pakete gibt es bald (danke, nion!), im uebrigen enthaelt Debian jetzt schon alle Pakete, von denen newsbeuter abhaengt, einem schnellen selbst compilieren steht also nichts im Weg.
Heute hab ich es mir nicht nehmen lassen, mir wieder mal technische Literatur zu kaufen, und zwar hab ich The Art of Software Security Assessment bestellt. Was man so hört, soll das Buch das detaillierteste und beste sein, das derzeit erhältlich ist. Das Sample Chapter sah zumindest sehr vielversprechend aus.
Überhaupt sind Code Audits die Richtung, in die ich mich in Zukunft privat mehr bewegen will. Gerade der 23C3, Fefes Ausführung über die Codequalität von GnuPG und ein Advisory zu einer Sicherheitslücke, die ich mit einem Freund inklusive PoC-Exploit veröffentlicht habe (natürlich unter Pseudonym; das Advisory hat sogar schon eine CVE-Nummer...) haben mich auf den Geschmack gebracht, wesentlich mehr Zeit in das Suchen und Beseitigen von Sicherheitslücken zu investieren, sowie überhaupt mehr Code zu lesen (obwohl ich das doch regelmäßig tue) und kritisch zu betrachten.
Es soll ja alte Systeme geben, auf den die Funktion strdup() noch nicht vorhanden ist. Sun OS 3 ist angeblich so ein Kandidat. Nun, was macht man, wenn man diese Funktion nicht zur Verfuegung hat? Man implementiert sie selbst. Und hier ist das grosse Problem: kaum einer macht es richtig. Vor ein paar Wochen hab ich einen Patch an den fetchmail-Maintainer (gluecklicherweise hat ESR das abgegeben) deswegen geschickt. Und was sehe ich heute in der APR (die Apache Portable Runtime)? Dieses da:
#if !APR_HAVE_STRDUP
char *strdup(constchar *str)
{
char *sdup;
size_t len = strlen(str) + 1;
sdup = (char *) malloc(len);
memcpy(sdup, str, len);
return sdup;
}
#endif
Zum Haare raufen. Also bin ich auf die Idee gekommen, einfach Google Codesearch zu verwenden, um eigene strdup()-Implementierungen zu finden. Tja, und das ging ziemlich schnell. Hier z.B..
char*
enca_strdup(constchar *s){
if(s == NULL)
returnNULL;
else
return strcpy(enca_malloc(strlen(s) + 1), s);
}
enca_malloc() enthaelt uebrigens keinerlei Pruefung auf NULL, ausser ein assert(), d.h. wenn man mit CFLAGS=-DNDEBUG compiled, und in eine OOM-Situation geraet, fuehrt das zum gleichen Mist. Und auch MySQL sieht sehr vertrauenswuerdig aus:
#ifndef HAVE_STRDUP
char *
strdup(constchar *s){
void *p2;
p2 = malloc(strlen(s)+1);
strcpy(p2, s);
return p2;
}
#endif
Und das in einer Datenbank von angeblicher Produktionsqualitaet. *ts*
Fazit: drei strdup()-Implementierungen begutachtet, keine einzige macht es richtig.
Dieses Wochenende hab ich mir Zeit genommen, um weiter an noos, meinem RSS-Feedreader zu arbeiten. Die Features werden mehr, und mittlerweile ist noos schon in einem Zustand, wo ich sagen muss, dass ich den schon gerne verwende. Und um meine werten Leser up-to-date zu halten, zeige ich eine kleine "Fotostrecke" mit den neuesten Screenshots.
Feed overview
Article overview
Article view
Save dialog
Wenn man die Screenshots mit vergangenen noos-Screenshots vergleicht, so fallen sicher ein paar kleine Unterschiede auf. Neu dazugekommen ist, dass man Artikel nun auch als Textdatei abspeichern kann, inklusive schönem Save-Dialog. Was man nicht so gut sehen kann, ist, dass mittlerweile das Keymapping über die Konfiguration frei einstellbar ist, und die aktuelle Einstellung auch in Keymap-Hints unten angezeigt wird. An diversen Stellen sind Verbesserungen des HTML-Rendering vorgenommen worden, und auch ansonsten ist etliches umstrukturiert und refactored worden. Es gibt auch einen einfachen Help-Dialog (kein Screenshot vorhanden), und Dokumentation ist ebenfalls schon ausgearbeitet. Vor dem ersten Release will ich ein paar Codeteile allerdings noch sauberer und kompakter machen, dann wird's aber voraussichtlich auch gleich Debian-Pakete etc. zum Download geben.
Wie berichtet, arbeite ich ja an einem RSS-Feedreader für die Konsole. Die letzten paar Tage habe ich nun wieder etwas mehr Arbeit reingesteckt. Ein großes Manko bisher war ja das absolut schlechte HTML-Rendering. Inspiriert durch Martin habe ich einen minimalistischen XML Pull Parser in C++ gebaut, der gerade soviel kann, um das in RSS-Feeds transportierte (X)HTML brauchbar aufbereiten und darstellen zu können. Auf Basis von diesem habe ich dann einen minimalistischen HTML-Renderer entwickelt, der bisher zwar nur mit relativ wenig Tags was anfangen kann (a, br, img, blockquote, p), aber bei den getesteten Feeds ein ganz ansehnliches Ergebnis gebracht hat. Nebenbei hab ich dann auch noch die Darstellungsprobleme von Umlauten behoben (libidn macht die Arbeit, für die, die sich nicht mit iconv, wcstombs und Konsorten herumschlagen wollen) und den Konfigurationsparser in einen verwendbaren Zustand gebracht. noos entwickelt sich, und ich hoffe, zur Zeit des 23C3 bei den anwesenden Texttool-Evangelisten erfolgreich missionieren zu können.