Rdesktopi ja xrdp testimine PVS-Studio analĂŒsaatori abil

rdesktopi ja xrdp kontrollimine PVS-Studio analĂŒsaatoriga
See on teine ĂŒlevaade RDP-protokolli avatud rakenduste kontrollimise artiklite sarjast. Selles kĂ€sitleme rdesktopi klienti ja xrdp serverit.

Vigade tuvastamiseks kasutati tööriista PVS-Studio. See on staatiline analĂŒsaator keelte C, C++, C# ja Java jaoks, mis on saadaval Windowsi, Linuxi ja macOSi platvormidel.

Artiklis on esitatud vaid need vead, mis tundusid mulle huvitavad. Sellegipoolest on projektid pisikesed, seega oli vigu ka vÀhe :).

MĂ€rkus. Eelmist artiklit FreeRDP projekti kohta saab leida siin.

rdesktop

rdesktop — avatud RDP kliendi rakendus UNIX-pĂ”histe sĂŒsteemide jaoks. Seda saab kasutada ka Windowsis, kui koguda projekt Cygwini all. litsentseeritud GPLv3 all.

See klient on vĂ€ga populaarne — see on vaikimisi kasutusel ReactOS-is, samuti on sellele saadaval kolmandate osapoolte graafilised front-end'id. Sellegipoolest on see ĂŒsna vana: esimene versioon ilmus 4. aprillil 2001 — artikli kirjutamise ajal on selle vanus 17 aastat.

Kuidas ma juba varasemalt mÀrkisin, on projekt tÀiesti pisike. See sisaldab umbes 30 tuhat koodireaga, mis on natuke vÀhe, arvestades selle vanust. VÔrdluseks, FreeRDP sisaldab 320 tuhat koodireaga. Siin on Cloci programmi vÀljund:

rdesktopi ja xrdp kontrollimine PVS-Studio analĂŒsaatoriga

Üksikasjalik kood

V779 Unreachable code detected. It is possible that an error is present. 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);
}

Viga ootab meid kohe funktsioonis main: nĂ€eme koodi, mis jĂ€rgneb operaatorile return — see fragment teostab mĂ€lu puhastamist. Sellegipoolest ei kujuta viga ohtu: kogu jaotatud mĂ€lu puhastab operatsioonisĂŒsteem pĂ€rast programmi lĂ”petamist.

Vigade töötlemise puudumine

V557 Array underrun is possible. The value of ‘n’ index could reach -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] = ' '; // <=
    str_handle_lines(output, &rest, linehandler, data);
  }
  ....
}

Antud koodi fragment loeb faili puhverisse, kuni fail on lÔppenud. Siiski puudub siin vigade töötlemine: kui midagi lÀheb valesti, siis lugema tagastab -1, ja siis toimub massiivi piirist vÀljumine output.

EOF kasutamine char tĂŒĂŒbis

V739 EOF ei tohiks vĂ”rrelda ‘char’ tĂŒĂŒbi vÀÀrtusega. ‘(c = fgetc(fp))’ peaks olema ‘int’ tĂŒĂŒpi. 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++;
  }
  ....
}

Siin nĂ€eme vale faili lĂ”ppu kĂ€itlemist: kui fgetc tagastab sĂŒmboli, mille kood on 0xFF, siis tĂ”lgendatakse seda kui faili lĂ”ppu (EOF).

EOF see on konstants, mis on tavaliselt mÀÀratletud kui -1. NĂ€iteks CP1251 kodeeringus on venekeelse tĂ€hestiku viimane tĂ€ht koodiga 0xFF, mis vastab numbrile -1, kui rÀÀgime muutuja tĂŒĂŒbist char. Tulemuseks on, et sĂŒmbol 0xFF, nagu ka EOF (-1) tĂ”lgendatakse faili lĂ”ppena. Selliste vigade vĂ€ltimiseks tuleks funktsiooni fgetc tulemus salvestada muutuja tĂŒĂŒbiga int.

TrĂŒkivead

Fragment 1

V547 VĂ€ljend ‘write_time’ on alati vale. 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; 
  ....
}

VÔib-olla autor segas siin || ja && tingimustes. Vaatame vÔimalikke vÀÀrtuste variante write_time ja change_time:

  • MĂ”lemad muutujad on vĂ”rdsed 0: sel juhul satume harusse muul juhul: muutuja mod_time on alati 0, olenemata jĂ€rgnevast tingimusest.
  • Üks muutuja on 0: mod_time on 0 (eeldades, et teine muutuja on mitte-negatiivne), sest MIN valib kahest valikust vĂ€iksema.
  • MĂ”lemad muutujad ei ole vĂ”rdsed 0: valime minimaalne vÀÀrtuse.

Kui tingimuse asendame write_time && change_time kÀitumine tundub korrektne:

  • Üks vĂ”i mĂ”lemad muutujad ei ole 0: valime mitte-null vÀÀrtuse.
  • MĂ”lemad muutujad ei ole vĂ”rdsed 0: valime minimaalne vÀÀrtuse.

Fragment 2

V547 VĂ€ljend on alati tĂ”ene. TĂ”enĂ€oliselt tuleks kasutada ‘&&’ operaatorit. 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;
  ....
}

Ilmselt on siin samuti operaatorid segamini aetud || ja &&, vÔi == ja !=: muutuja ei saa korraga vÔtta vÀÀrtusi 20 ja 9.

Piiramatu stringi koopia

V512 ‘sprintf’ funktsiooni kutsumine viib ‘fullpath’ puhveri ĂŒlevooluni. disk.c 1257

RD_NTSTATUS
disk_query_directory(....)
{
  ....
  char *dirname, fullpath[PATH_MAX];
  ....
  /* Saame teavet katalooge sisenemise kohta */
  sprintf(fullpath, "%s/%s", dirname, pdirent->d_name);
  ....
}

Funktsiooni tĂ€ielikku kaalumist vaadates saame aru, et see kood ei pĂ”hjusta probleeme. Kuid tulevikus vĂ”ivad need tekkida: ĂŒks tĂ€helepanematult tehtud muudatus ja saame puhveri ĂŒlevoolu — sprintf ei ole piiratud, seega kadreerimise ajal teede liitmine vĂ”ib viia massiivi piiride ĂŒletamiseni. Soovitame selle kutse tĂ€hele panna snprintf(fullpath, PATH_MAX, 
.).

Liigne tingimus

V560 Osaline tingimus, mis on alati tÔene: lisa > 0. scard.c 507

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

Kontrollimine add > 0 siin ei ole mÔtet: muutuja on alati suurem kui null, sest read % 4 tagastab jagamise jÀÀgi, mis ei saa kunagi olla 4.

xrdp

xrdp — avatud lĂ€htekoodiga RDP serveri teostus. Projekt on jagatud kaheks osaks:

  • xrdp — protokolli teostus. Levitatakse Apache 2.0 litsentsi alusel.
  • xorgxrdp — Xorgi draiverite komplekt, mida saab kasutada koos xrdp-ga. Litsents — X11 (nagu MIT, kuid reklaami kasutamine on keelatud)

Projekti arendus pĂ”hineb rdesktopi ja FreeRDP tulemuste pĂ”hjal. Alguses tuli graafika jaoks kasutada eraldi VNC serverit vĂ”i spetsiaalset X11 serverit RDP toe jaoks — X11rdp, kuid xorgxrdp tekkimisega ei olnud seda enam vajalik.

Selles artiklis me xorgxrdp-st ei rÀÀgi.

Projekt xrdp, nagu eelmine, on ĂŒsna vĂ€ike ja sisaldab umbes 80 tuhat rida.

rdesktopi ja xrdp kontrollimine PVS-Studio analĂŒsaatoriga

Veel trĂŒkivigu

V525 Kood sisaldab sarnaste plokkide kogumit. Kontrollige elemente 'r', 'g', 'r' ridadel 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++;
      }
      ....
  }
  ....
}

See kood on vĂ”etud librfxcodec teeki, mis implementeerib jpeg2000 koodeki RemoteFX-i jaoks. Siin paistavad olevat segi aetud graafikandmete kanalid — sinise vĂ€rvi asemel kirjutatakse punane. Selline viga tekkis tĂ”enĂ€oliselt copy-paste'i tulemusena.

Sama probleem esines ka sarnases funktsioonis rfx_encode_format_argb, millest teatas meile ka analĂŒsaator:

V525 Kood sisaldab sarnaste plokkide kogumit. Kontrollige elemente 'a', 'r', 'g', 'r' ridadel 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++;
}

Massiivi deklareerimine

V557 Massiivi ĂŒletamine on vĂ”imalik. 'i - 8' indeksi vÀÀrtus vĂ”ib ulatuda 129-ni. 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];
    ....
  }
  ....
}

Massiivi deklareerimine ja mÀÀramine nendes kahes failis ei ole ĂŒhilduvad — suurus erineb 1 vĂ”rra. Kuid vigu ei esine — failis evdev-map.c on nĂ€idatud Ă”ige suurus, nii et ĂŒletamisi ei toimu. Seega on see lihtsalt puudujÀÀk, mis on kergesti parandatav.

Vale vÔrdlemine

V560 Osa tingimuslikust vÀljendusest on alati vale: (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))
  {
    ....
  }
  ....
}

Funktsioonis toimub muutuja tĂŒĂŒbi lugemine unsigned short tĂŒĂŒpi muutuja intKontrolli siin pole vajalik, kuna me loeme alla 0 tĂŒĂŒpi muutuja ja mÀÀrame tulemuse suurema suurusega muutujale, seetĂ”ttu ei saa muutuja vĂ”tta negatiivset vÀÀrtust.

Üksusel ei ole ĂŒhtki kontrolli

V560 Osaliselt tingimuslikus vÀljendis on alati tÔene: (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: viga");
      return 1;
  }
  ....
}

Kontrollid mittevĂ”rdsuse ĂŒle siin ei ole mĂ”tet, kuna meil on juba vĂ”rreldav alguses. On ĂŒsna tĂ”enĂ€oline, et see on trĂŒkiviga ja arendaja tahtis kasutada operaatorit || valeargumentide filtreerimiseks.

KokkuvÔte

Kontrollimisel ei tuvastatud tĂ”siseid vigu, kuid leiti palju puudusi. Sellegipoolest kasutatakse neid projekte paljudes sĂŒsteemides, kuigi need on oma mahult vĂ€ikesed. VĂ€ikeses projektis ei pea tingimata olema palju vigu, seetĂ”ttu ei saa analĂŒsaatori tööd hinnata ainult vĂ€ikeste projektide pĂ”hjal. Selle kohta saate rohkem lugeda artiklis "Tunded, mis numbritega kinnitust leidis«.

Saate alla laadida PVS-Studio prooviversiooni meie juures veebisaidil.

rdesktopi ja xrdp kontrollimine PVS-Studio analĂŒsaatoriga

Kui soovite seda artiklit jagada ingliskeelsele publikule, siis palun kasutage tÔlke linki: Sergey Larin. rdesktopi ja xrdp kontrollimine PVS-Studio abil

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster