
See on teine ĂŒlevaade RDP-protokolli avatud rakenduste kontrollimise artiklite sarjast. Selles kĂ€sitleme rdesktopi klienti ja xrdp serverit.
Vigade tuvastamiseks kasutati tööriista . 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 .
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:

Ăksikasjalik kood
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
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
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
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
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
â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
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
â 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.

Veel trĂŒkivigu
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:
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
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
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
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 "«.
Saate alla laadida PVS-Studio prooviversiooni meie juures .
Kui soovite seda artiklit jagada ingliskeelsele publikule, siis palun kasutage tÔlke linki: Sergey Larin.
Allikas: habr.com
