Dies ist die zweite Rezension in einer Reihe von Artikeln über das Testen von Open-Source-Programmen für die Arbeit mit dem RDP-Protokoll. Darin werden wir uns den rdesktop-Client und den xrdp-Server ansehen.
Wird als Hilfsmittel zur Fehlererkennung verwendet
Der Artikel stellt nur die Fehler vor, die mir interessant erschienen. Da die Projekte jedoch klein sind, gab es nur wenige Fehler :).
Beachten. Einen früheren Artikel zur FreeRDP-Projektverifizierung finden Sie hier
rdesktop
Dieser Client ist sehr beliebt – er wird standardmäßig in ReactOS verwendet, und Sie können auch grafische Frontends von Drittanbietern dafür finden. Allerdings ist er schon ziemlich alt: Seine erste Veröffentlichung fand am 4. April 2001 statt – zum Zeitpunkt des Verfassens dieses Artikels ist er 17 Jahre alt.
Wie ich bereits erwähnt habe, ist das Projekt sehr klein. Es enthält etwa 30 Codezeilen, was angesichts seines Alters etwas seltsam ist. Zum Vergleich: FreeRDP enthält 320 Zeilen. Hier ist die Ausgabe des Cloc-Programms:
Unerreichbarer Code
int
main(int argc, char *argv[])
{
....
return handle_disconnect_reason(deactivated, ext_disc_reason);
if (g_redirect_username)
xfree(g_redirect_username);
xfree(g_username);
}
Der Fehler begegnet uns sofort in der Funktion Haupt-: Wir sehen den Code, der nach dem Operator kommt Rückkehr – Dieses Fragment führt eine Speicherbereinigung durch. Der Fehler stellt jedoch keine Gefahr dar: Der gesamte zugewiesene Speicher wird vom Betriebssystem gelöscht, nachdem das Programm beendet wurde.
Keine Fehlerbehandlung
RD_BOOL
subprocess(char *const argv[], str_handle_lines_t linehandler, void *data)
{
int n = 1;
char output[256];
....
while (n > 0)
{
n = read(fd[0], output, 255);
output[n] = ' '; // <=
str_handle_lines(output, &rest, linehandler, data);
}
....
}
Der Codeausschnitt liest in diesem Fall aus der Datei in einen Puffer, bis die Datei endet. Allerdings gibt es hier keine Fehlerbehandlung: Wenn etwas schief geht, dann lesen gibt -1 zurück und dann wird das Array überrannt Möglichkeiten für das Ausgangssignal:.
Verwendung von EOF im char-Typ
int
ctrl_send_command(const char *cmd, const char *arg)
{
char result[CTRL_RESULT_SIZE], c, *escaped;
....
while ((c = fgetc(fp)) != EOF && index < CTRL_RESULT_SIZE && c != 'n')
{
result[index] = c;
index++;
}
....
}
Hier sehen wir eine fehlerhafte Behandlung des Erreichens des Dateiendes: if fgetc Gibt ein Zeichen mit dem Code 0xFF zurück, es wird als Ende der Datei interpretiert (EOF).
EOF Es handelt sich um eine Konstante, die normalerweise als -1 definiert wird. In der CP1251-Kodierung hat der letzte Buchstabe des russischen Alphabets beispielsweise den Code 0xFF, was der Zahl -1 entspricht, wenn es sich um eine Variable wie handelt verkohlen. Es stellt sich heraus, dass das Symbol 0xFF ist EOF (-1) wird als Ende der Datei interpretiert. Um solche Fehler zu vermeiden, ist das Ergebnis der Funktion fgetc sollte in einer Variablen wie gespeichert werden int.
Tippfehler
Fragment 1
RD_NTSTATUS
disk_set_information(....)
{
time_t write_time, change_time, access_time, mod_time;
....
if (write_time || change_time)
mod_time = MIN(write_time, change_time);
else
mod_time = write_time ? write_time : change_time; // <=
....
}
Vielleicht hat der Autor dieses Codes etwas falsch verstanden || и && im Zustand. Betrachten wir mögliche Optionen für Werte write_time и change_time:
- Beide Variablen sind gleich 0: In diesem Fall landen wir in einer Verzweigung sonst: variabel mod_time wird immer 0 sein, unabhängig von der nachfolgenden Bedingung.
- Eine der Variablen ist 0: mod_time wird gleich 0 sein (vorausgesetzt, die andere Variable hat einen nicht negativen Wert), weil MIN wählt die kleinere der beiden Optionen.
- Beide Variablen sind ungleich 0: Wählen Sie den Mindestwert.
Beim Ersetzen der Bedingung durch write_time && change_time Das Verhalten wird korrekt aussehen:
- Eine oder beide Variablen sind ungleich 0: Wählen Sie einen Wert ungleich Null.
- Beide Variablen sind ungleich 0: Wählen Sie den Mindestwert.
Fragment 2
static RD_NTSTATUS
disk_device_control(RD_NTHANDLE handle, uint32 request, STREAM in,
STREAM out)
{
....
if (((request >> 16) != 20) || ((request >> 16) != 9))
return RD_STATUS_INVALID_PARAMETER;
....
}
Offenbar sind auch hier die Betreiber durcheinander || и &&Oder == и !=: Eine Variable kann nicht gleichzeitig den Wert 20 und 9 haben.
Unbegrenztes Zeilenkopieren
RD_NTSTATUS
disk_query_directory(....)
{
....
char *dirname, fullpath[PATH_MAX];
....
/* Get information for directory entry */
sprintf(fullpath, "%s/%s", dirname, pdirent->d_name);
....
}
Wenn man sich die Funktion vollständig ansieht, wird klar, dass dieser Code keine Probleme verursacht. Sie können jedoch in Zukunft auftreten: Eine unvorsichtige Änderung und wir bekommen einen Pufferüberlauf - sprintf ist durch nichts eingeschränkt, sodass wir beim Verketten von Pfaden über die Grenzen des Arrays hinausgehen können. Es wird empfohlen, diesen Aufruf auf zu beachten snprintf(vollständiger Pfad, PATH_MAX, ….).
Redundanter Zustand
static void
inRepos(STREAM in, unsigned int read)
{
SERVER_DWORD add = 4 - read % 4;
if (add < 4 && add > 0)
{
....
}
}
Проверка addiere > 0 Hier besteht keine Notwendigkeit: Die Variable wird immer größer als Null sein, weil Lesen Sie % 4 gibt den Rest der Division zurück, aber er wird niemals gleich 4 sein.
xrdp
- xrdp – Protokollimplementierung. Verteilt unter der Apache 2.0-Lizenz.
- xorgxrdp – Eine Reihe von Xorg-Treibern zur Verwendung mit xrdp. Lizenz – X11 (wie MIT, aber Verwendung in der Werbung verboten)
Die Entwicklung des Projekts basiert auf den Ergebnissen von rdesktop und FreeRDP. Um mit Grafiken zu arbeiten, musste man zunächst einen separaten VNC-Server oder einen speziellen X11-Server mit RDP-Unterstützung – X11rdp – verwenden, aber mit dem Aufkommen von xorgxrdp verschwand die Notwendigkeit dafür.
In diesem Artikel gehen wir nicht auf xorgxrdp ein.
Das xrdp-Projekt ist wie das vorherige sehr klein und enthält etwa 80 Zeilen.
Weitere Tippfehler
static int
rfx_encode_format_rgb(const char *rgb_data, int width, int height,
int stride_bytes, int pixel_format,
uint8 *r_buf, uint8 *g_buf, uint8 *b_buf)
{
....
switch (pixel_format)
{
case RFX_FORMAT_BGRA:
....
while (x < 64)
{
*lr_buf++ = r;
*lg_buf++ = g;
*lb_buf++ = r; // <=
x++;
}
....
}
....
}
Dieser Code wurde der Bibliothek librfxcodec entnommen, die den jpeg2000-Codec für RemoteFX implementiert. Hier sind offenbar die grafischen Datenkanäle verwechselt – statt der „blauen“ Farbe wird „rot“ aufgezeichnet. Dieser Fehler ist höchstwahrscheinlich auf das Kopieren und Einfügen zurückzuführen.
Das gleiche Problem trat in einer ähnlichen Funktion auf rfx_encode_format_argb, was uns der Analysator auch mitteilte:
while (x < 64)
{
*la_buf++ = a;
*lr_buf++ = r;
*lg_buf++ = g;
*lb_buf++ = r;
x++;
}
Array-Deklaration
// evdev-map.c
int xfree86_to_evdev[137-8+1] = {
....
};
// genkeymap.c
extern int xfree86_to_evdev[137-8];
int main(int argc, char **argv)
{
....
for (i = 8; i <= 137; i++) /* Keycodes */
{
if (is_evdev)
e.keycode = xfree86_to_evdev[i-8];
....
}
....
}
Die Deklaration und Definition des Arrays in diesen beiden Dateien sind inkompatibel – die Größe unterscheidet sich um 1. Es treten jedoch keine Fehler auf – die richtige Größe ist in der Datei evdev-map.c angegeben, es gibt also keine außerhalb der Grenzen liegenden Grenzen. Das ist also nur ein Fehler, der leicht behoben werden kann.
Falscher Vergleich
// common/parse.h
#if defined(B_ENDIAN) || defined(NEED_ALIGN)
#define in_uint16_le(s, v) do
....
#else
#define in_uint16_le(s, v) do
{
(v) = *((unsigned short*)((s)->p));
(s)->p += 2;
} while (0)
#endif
int
xrdp_caps_process_confirm_active(struct xrdp_rdp *self, struct stream *s)
{
int cap_len;
....
in_uint16_le(s, cap_len);
....
if ((cap_len < 0) || (cap_len > 1024 * 1024))
{
....
}
....
}
Die Funktion liest eine Typvariable unsigned short in eine Variable wie int. Eine Überprüfung ist hier nicht erforderlich, da wir eine vorzeichenlose Variable lesen und das Ergebnis einer größeren Variablen zuweisen, sodass die Variable keinen negativen Wert annehmen kann.
Unnötige Kontrollen
int EXPORT_CC
libxrdp_send_pointer(struct xrdp_session *session, int cache_idx,
char *data, char *mask, int x, int y, int bpp)
{
....
if ((bpp == 15) && (bpp != 16) && (bpp != 24) && (bpp != 32))
{
g_writeln("libxrdp_send_pointer: error");
return 1;
}
....
}
Ungleichheitsprüfungen machen hier keinen Sinn, da wir bereits zu Beginn einen Vergleich haben. Es ist wahrscheinlich, dass es sich hierbei um einen Tippfehler handelt und der Entwickler den Operator verwenden wollte || um ungültige Argumente herauszufiltern.
Abschluss
Bei der Prüfung wurden keine schwerwiegenden Fehler festgestellt, es wurden jedoch zahlreiche Mängel festgestellt. Diese Designs werden jedoch in vielen Systemen verwendet, wenn auch in geringem Umfang. Ein kleines Projekt weist nicht unbedingt viele Fehler auf, daher sollten Sie die Leistung des Analysators nicht nur bei kleinen Projekten beurteilen. Mehr dazu lesen Sie im Artikel „
Eine Testversion von PVS-Studio können Sie bei uns herunterladen
Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Übersetzungslink: Sergey Larin.
Source: habr.com