Validation de rdesktop et xrdp à l'aide de l'analyseur PVS-Studio

Image3

Il s'agit de la deuxième revue d'une série d'articles sur la vérification des programmes open source pour travailler avec le protocole RDP. Dans ce document, nous considérerons le client rdesktop et le serveur xrdp.

PVS-Studio a été utilisé comme un outil pour détecter les erreurs. Il s'agit d'un analyseur de code statique pour C, C ++, C # et Java, disponible sur Windows, Linux et macOS.

L'article ne présente que les erreurs qui m'ont paru intéressantes. Cependant, les projets sont petits, il y a donc eu peu d'erreurs :).

Remarque L'article précédent sur la vérification du projet FreeRDP peut être trouvé ici .

rdesktop


rdesktop est une implémentation gratuite du client RDP pour les systèmes UNIX. Il peut également être utilisé sous Windows, si vous générez un projet sous Cygwin. Sous licence GPLv3.

Ce client est très populaire - il est utilisé par défaut dans ReactOS, et vous pouvez également y trouver des frontaux graphiques tiers. Néanmoins, il est assez vieux: la première sortie a eu lieu le 4 avril 2001 - au moment de la rédaction, son âge était de 17 ans.

Comme je l'ai noté précédemment, le projet est très petit. Il contient environ 30 000 lignes de code, ce qui est un peu étrange compte tenu de son âge. A titre de comparaison, FreeRDP contient 320 mille lignes. Voici la sortie du programme Cloc:

Image1


Code inaccessible


V779 Code inaccessible détecté. Il est possible qu'une erreur soit présente. rdesktop.c 1502

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); } 

L'erreur nous rencontre immédiatement dans la fonction principale : nous voyons le code qui vient après l' instruction de retour - ce fragment nettoie la mémoire. Néanmoins, l'erreur ne constitue pas une menace: toute la mémoire allouée est nettoyée par le système d'exploitation après la fin du programme.

Manque de gestion des erreurs


V557 Le sous-ensemble de la baie est possible. La valeur de l'indice 'n' pourrait atteindre -1. rdesktop.c 1872

 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] = '\0'; // <= str_handle_lines(output, &rest, linehandler, data); } .... } 

L'extrait de code dans ce cas lit le fichier dans le tampon jusqu'à la fin du fichier. Cependant, il n'y a pas de gestion d'erreur ici: si quelque chose se passe mal, alors read renverra -1, puis le tableau de sortie dépassera les limites du tableau.

Utilisation d'EOF dans char


V739 EOF ne doit pas être comparé à une valeur de type 'char'. Le '(c = fgetc (fp))' doit être de type 'int'. ctrl.c 500

 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++; } .... } 

Nous voyons ici un traitement incorrect pour atteindre la fin du fichier: si fgetc renvoie un caractère dont le code est 0xFF, alors il sera interprété comme la fin du fichier ( EOF ).

EOF est une constante, généralement définie comme -1. Par exemple, dans le codage CP1251, la dernière lettre de l'alphabet russe a le code 0xFF, qui correspond au nombre -1, si nous parlons d'une variable de type char . Il s'avère que le caractère 0xFF, comme EOF (-1), est perçu comme la fin du fichier. Pour éviter de telles erreurs, le résultat de la fonction fgetc doit être stocké dans une variable int .

Typos


Fragment 1

V547 L' expression 'write_time' est toujours fausse. disk.c 805

 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; // <= .... } 

L'auteur de ce code est peut-être confus || et && fournis. Considérez les options possibles pour write_time et change_time :

  • Les deux variables sont 0: dans ce cas, nous arrivons à la branche else : la variable mod_time sera toujours 0 quelle que soit la condition suivante.
  • L'une des variables est 0: mod_time sera 0 (à condition que l'autre variable ait une valeur non négative), car MIN choisira la plus petite des deux options.
  • Les deux variables ne sont pas égales à 0: nous sélectionnons la valeur minimale.

Lors du remplacement d'une condition par write_time && change_time, le comportement sera correct:
  • Une ou les deux variables ne sont pas égales à 0: sélectionnez une valeur différente de zéro.
  • Les deux variables ne sont pas égales à 0: nous sélectionnons la valeur minimale.

Fragment 2

V547 L' expression est toujours vraie. L'opérateur '&&' devrait probablement être utilisé ici. disk.c 1419

 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; .... } 

Apparemment, les opérateurs || et && , soit == et ! = : la variable ne peut pas prendre les valeurs 20 et 9 en même temps.

Copie de ligne illimitée


V512 Un appel de la fonction 'sprintf' entraînera un débordement de la mémoire tampon 'fullpath'. disk.c 1257

 RD_NTSTATUS disk_query_directory(....) { .... char *dirname, fullpath[PATH_MAX]; .... /* Get information for directory entry */ sprintf(fullpath, "%s/%s", dirname, pdirent->d_name); .... } 

En considérant la fonction, il deviendra complètement clair que ce code ne pose pas de problème. Cependant, ils peuvent survenir à l'avenir: un changement imprudent et nous obtenons un débordement de tampon - sprintf n'est limité par rien, donc lors de la concaténation de chemins, nous pouvons aller au-delà des limites du tableau. Il est recommandé de noter cet appel sur snprintf (fullpath, PATH_MAX, ....) .

État redondant


V560 Une partie de l'expression conditionnelle est toujours vraie: ajoutez> 0. scard.c 507

 static void inRepos(STREAM in, unsigned int read) { SERVER_DWORD add = 4 - read % 4; if (add < 4 && add > 0) { .... } } 

La vérification de l' addition> 0 est inutile: la variable sera toujours supérieure à zéro, car la lecture% 4 renverra le reste de la division, et elle ne sera jamais 4.

xrdp


xrdp est une implémentation de serveur RDP open source. Le projet est divisé en 2 parties:

  • xrdp - implémentation du protocole. Distribué sous la licence Apache 2.0.
  • xorgxrdp - Un ensemble de pilotes Xorg à utiliser avec xrdp. Licence - X11 (comme MIT, mais interdit l'utilisation dans la publicité)

Le développement du projet est basé sur les résultats de rdesktop et FreeRDP. Au départ, je devais utiliser un serveur VNC distinct pour travailler avec les graphiques, ou un serveur X11 spécial avec prise en charge RDP - X11rdp, mais avec l'avènement de xorgxrdp, ils n'étaient plus nécessaires.

Dans cet article, nous ne parlerons pas de xorgxrdp.

Le projet xrdp, comme le précédent, est très petit et contient environ 80 000 lignes.

Image2


Plus de fautes de frappe


V525 Le code contient la collection de blocs similaires. Vérifiez les éléments «r», «g», «r» dans les lignes 87, 88, 89. rfxencode_rgb_to_yuv.c 87

 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++; } .... } .... } 

Ce code a été extrait de la bibliothèque librfxcodec qui implémente le codec jpeg2000 pour que RemoteFX fonctionne. Ici, apparemment, les canaux de données graphiques sont mélangés - au lieu de la couleur «bleue», «rouge» est écrit. Une telle erreur est probablement apparue à la suite d'un copier-coller.

Le même problème est tombé dans une fonction similaire rfx_encode_format_argb , dont l'analyseur nous a également parlé:

V525 Le code contient la collection de blocs similaires. Vérifiez les éléments «a», «r», «g», «r» dans les lignes 260, 261, 262, 263. rfxencode_rgb_to_yuv.c 260

 while (x < 64) { *la_buf++ = a; *lr_buf++ = r; *lg_buf++ = g; *lb_buf++ = r; x++; } 

Déclaration de tableau


V557 Le dépassement de matrice est possible. La valeur de l'indice «i - 8» pourrait atteindre 129. genkeymap.c 142

 // 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]; .... } .... } 

La déclaration et la définition du tableau dans ces deux fichiers sont incompatibles - la taille diffère de 1. Cependant, aucune erreur ne se produit - la taille correcte est spécifiée dans le fichier evdev-map.c, il n'y a donc aucun moyen de sortir des limites. Ce n'est donc qu'un défaut facile à corriger.

Comparaison non valide


V560 Une partie de l'expression conditionnelle est toujours fausse: (cap_len <0). xrdp_caps.c 616

 // 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)) { .... } .... } 

Dans une fonction, une variable de type unsigned short est lue dans une variable de type int . La vérification n'est pas nécessaire ici, car nous lisons une variable d'un type non signé et affectons le résultat à une variable plus grande, de sorte que la variable ne peut pas prendre une valeur négative.

Contrôles inutiles


V560 Une partie de l'expression conditionnelle est toujours vraie: (bpp! = 16). libxrdp.c 704

 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; } .... } 

Les vérifications de l'inégalité n'ont pas de sens ici, car nous avons déjà une comparaison au début. Il s'agit probablement d'une faute de frappe et le développeur a voulu utiliser le || pour filtrer les arguments invalides.

Conclusion


Au cours de l'inspection, aucune erreur grave n'a été détectée, mais de nombreuses lacunes ont été constatées. Cependant, ces projets sont utilisés dans de nombreux systèmes, quoique de petite envergure. Un petit projet ne doit pas avoir beaucoup d'erreurs, vous ne devez donc pas juger le travail de l'analyseur uniquement sur de petits projets. Vous pouvez en savoir plus à ce sujet dans l'article " Sensations confirmées par des chiffres ".

Vous pouvez télécharger une version d'essai de PVS-Studio sur notre site Web .



Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Sergey Larin. Vérification de rdesktop et xrdp avec PVS-Studio

Source: https://habr.com/ru/post/fr447880/


All Articles