Rdesktop en xrdp controleren met behulp van de PVS-Studio-analysator

Controleren van rdesktop en xrdp met behulp van de PVS-Studio-analyzer
Dit is het tweede overzicht in een reeks artikelen over het controleren van opensourceprogramma's op geschiktheid voor het RDP-protocol. Hierin kijken we naar de rdesktop-client en de xrdp-server.

Het hulpmiddel dat werd gebruikt om fouten te identificeren was PVS StudioHet is een statische code-analysator voor C, C++, C# en Java, beschikbaar op verschillende platformen. Windows, Linux и macOS.

Het artikel vermeldt alleen de fouten die ik interessant vind. Het zijn echter kleine projecten, dus er zijn weinig fouten gemaakt :).

Noot. Een eerder artikel over de FreeRDP-projectbeoordeling is te vinden hier.

desktop

desktop — свободная реализация клиента RDP для UNIX-based систем. Его также можно использовать и под Windows, если собирать проект под Cygwin. Лицензирован под GPLv3.

Deze client is erg populair: deze wordt standaard gebruikt in ReactOS en er zijn ook grafische front-ends van derden voor beschikbaar. Het is echter al behoorlijk oud: de eerste release was op 4 april 2001, dus op het moment van schrijven is het 17 jaar oud.

Zoals ik al eerder zei, het project is erg klein. Het bevat ongeveer 30 regels code, wat vreemd is gezien de leeftijd. Ter vergelijking: FreeRDP bevat 320 duizend regels. Dit is de uitvoer van het Cloc-programma:

Controleren van rdesktop en xrdp met behulp van de PVS-Studio-analyzer

Onbereikbare code

V779 Onbereikbare code gedetecteerd. Het is mogelijk dat er een fout is opgetreden. 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);
}

De fout komt ons direct in de functie tegen hoofd-: we zien de code die na de operator komt terugkeer - dit fragment voert geheugenwissing uit. De fout vormt echter geen bedreiging: nadat het programma is beëindigd, wordt het gehele toegewezen geheugen door het besturingssysteem gewist.

Geen foutverwerking

V557 Array-underrun is mogelijk. De waarde van index 'n' kan -1 bereiken. 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] = ' '; // <=
    str_handle_lines(output, &rest, linehandler, data);
  }
  ....
}

In dit geval leest het codefragment het bestand in de buffer totdat het bestand is afgelopen. Er is hier echter geen sprake van foutafhandeling: als er iets fout gaat, dan dit artikel lezen zal -1 retourneren, en dan zal de array buiten de grenzen zijn uitvoer.

EOF gebruiken in char-type

V739 EOF mag niet worden vergeleken met een waarde van het type 'char'. '(c = fgetc(fp))' moet van het type 'int' zijn. 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++;
  }
  ....
}

Hier zien we een onjuiste afhandeling van het bereiken van het einde van het bestand: als fgetc retourneert een teken waarvan de code 0xFF is, dan wordt dit gezien als het einde van het bestand (EOF).

EOF Dit is een constante, meestal gedefinieerd als -1. In de CP1251-codering heeft de laatste letter van het Russische alfabet bijvoorbeeld de code 0xFF, wat overeenkomt met het getal -1 als we het hebben over een variabele van het type verkolen. Het blijkt dat het symbool 0xFF, zoals EOF (-1) wordt beschouwd als het einde van het bestand. Om dergelijke fouten te voorkomen, moet het resultaat van de functie fgetc moet worden opgeslagen in een variabele van het type int.

Typefouten

Fragment 1

V547 Expressie 'write_time' is altijd false. schijf.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; // <=
  ....
}

Misschien heeft de auteur van deze code het verkeerd begrepen || и && in de staat. Laten we mogelijke waarden overwegen schrijftijd и veranderingstijd:

  • Beide variabelen zijn 0: in dit geval komen we bij de tak anders:variabele mod_tijd zal altijd gelijk zijn aan 0, ongeacht de daaropvolgende voorwaarde.
  • Eén van de variabelen is gelijk aan 0: mod_tijd zal gelijk zijn aan 0 (op voorwaarde dat de andere variabele een niet-negatieve waarde heeft), omdat MIN kiest de kleinste van de twee opties.
  • Beide variabelen zijn niet gelijk aan 0: kies de minimumwaarde.

Bij het vervangen van de voorwaarde door schrijftijd && verandertijd Het gedrag zal er correct uitzien:

  • Eén of beide variabelen zijn niet gelijk aan 0: kies een waarde die niet nul is.
  • Beide variabelen zijn niet gelijk aan 0: kies de minimumwaarde.

Fragment 2

V547 Expressie is altijd waar. Waarschijnlijk moet hier de operator '&&' worden gebruikt. schijf.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;
  ....
}

Blijkbaar zijn de operatoren hier ook verwisseld. || и &&Of == и !=: een variabele kan niet tegelijkertijd de waarde 20 en 9 hebben.

Onbeperkt kopiëren van strings

V512 Een aanroep van de functie 'sprintf' leidt tot een overloop van de buffer 'fullpath'. schijf.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);
  ....
}

Wanneer je de functie in zijn geheel bekijkt, wordt het duidelijk dat deze code geen problemen oplevert. Ze kunnen zich echter in de toekomst voordoen: één onzorgvuldige wijziging en we krijgen een bufferoverloop - sprint wordt door niets beperkt, dus bij het samenvoegen van paden kunnen we buiten de grenzen van de array gaan. Het is aan te raden om deze oproep op te merken snprintf(volledig pad, PATH_MAX, ….).

Overbodige toestand

V560 Een deel van de voorwaardelijke expressie is altijd waar: add > 0. scard.c 507

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

Проверка toevoegen > 0 hier is geen punt: de variabele zal altijd groter zijn dan nul, omdat lees % 4 retourneert de restwaarde van de deling en zal nooit gelijk zijn aan 4.

xrdp

xrdp — open source RDP-serverimplementatie. Het project is verdeeld in twee delen:

  • xrdp - protocol implementatie. Gedistribueerd onder de Apache 2.0-licentie.
  • xorgxrdp - een set Xorg-stuurprogramma's voor gebruik met xrdp. Licentie - X11 (zoals MIT, maar gebruik in advertenties is verboden)

De ontwikkeling van het project is gebaseerd op de resultaten van rdesktop en FreeRDP. In eerste instantie was het voor het werken met graphics nodig om een ​​aparte VNC-server te gebruiken, of een speciale X11-server met RDP-ondersteuning (X11rdp). Met de komst van xorgxrdp verdween echter de behoefte hieraan.

In dit artikel gaan we niet in op xorgxrdp.

Het xrdp-project is net als het vorige vrij klein en bevat ongeveer 80 duizend regels.

Controleren van rdesktop en xrdp met behulp van de PVS-Studio-analyzer

Meer typefouten

V525 De code bevat de verzameling van vergelijkbare blokken. Controleer items 'r', 'g', 'r' in regels 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++;
      }
      ....
  }
  ....
}

Deze code is afkomstig uit de librfxcodec-bibliotheek, die de jpeg2000-codec voor RemoteFX implementeert. Hier zijn de grafische gegevenskanalen blijkbaar verwisseld: in plaats van de kleur 'blauw' wordt 'rood' opgenomen. Deze fout is waarschijnlijk ontstaan ​​door kopiëren en plakken.

Hetzelfde probleem is ook in een soortgelijke functie opgetreden rfx_encode_format_argb, waar de analysator ons ook over vertelde:

V525 De code bevat de verzameling van vergelijkbare blokken. Controleer items 'a', 'r', 'g', 'r' in regels 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++;
}

Array-declaratie

V557 Array overrun is mogelijk. De waarde van de index 'i — 8' kan 129 bereiken. 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];
    ....
  }
  ....
}

De declaratie en definitie van de array in deze twee bestanden zijn niet compatibel: de grootte verschilt met 1. Er doen zich echter geen fouten voor: de juiste grootte is opgegeven in het bestand evdev-map.c, dus er treedt geen out-of-bounds-fout op. Het is dus gewoon een fout die gemakkelijk opgelost kan worden.

Onjuiste vergelijking

V560 Een deel van de voorwaardelijke expressie is altijd onwaar: (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))
  {
    ....
  }
  ....
}

De functie leest een variabele van het type ongetekend kort in een variabele van het type int. Hier is geen controle nodig, omdat we een ongetekende variabele lezen en het resultaat aan een grotere variabele toewijzen. De variabele kan dus geen negatieve waarde aannemen.

Onnodige controles

V560 Een deel van de voorwaardelijke expressie is altijd waar: (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;
  }
  ....
}

Ongelijkheidstesten hebben hier geen zin, omdat we aan het begin al een vergelijking hebben. Het is goed mogelijk dat dit een typefout is en dat de ontwikkelaar de operator wilde gebruiken || om ongeldige argumenten eruit te filteren.

Conclusie

Tijdens de audit werden geen ernstige fouten geconstateerd, maar er werden wel veel tekortkomingen vastgesteld. Toch worden deze projecten in veel systemen gebruikt, zij het in kleinere omvang. Een klein project bevat niet noodzakelijkerwijs veel fouten. Daarom moet u de prestaties van de analyzer niet alleen op kleine projecten beoordelen. Meer hierover kunt u lezen in het artikel “Gevoelens die door cijfers werden bevestigd".

U kunt een proefversie van PVS-Studio bij ons downloaden op Online.

Controleren van rdesktop en xrdp met behulp van de PVS-Studio-analyzer

Als u dit artikel met een Engelssprekend publiek wilt delen, gebruik dan de vertaallink: Sergey Larin. Rdesktop en xrdp controleren met PVS-Studio

Bron: www.habr.com

Koop betrouwbare hosting voor sites met DDoS-bescherming, VPS VDS-servers 🔥 Koop betrouwbare websitehosting met DDoS-bescherming, VPS- en VDS-servers | ProHoster