
See on teine ĂŒlevaade artiklite sarjast avatud programmide kontrollimise kohta RDP-protokolliga. Selles vaatleme rdesktop kliendi ja xrdp serveri funktsioone.
Vigade avastamiseks kasutati tööriista . See on staatiline koodianalĂŒsaator C, C++, C# ja Java keelte jaoks, mis on saadaval Windowsi, Linuxi ja macOS-i platvormidel.
Artiklis esitatakse vaid need vead, mis mulle huvitavad tundusid. TÔele au andes on projektid siiski vÀikesed, seega on ka vigu olnud vÀhe :)
MĂ€rkus. Eelmist artiklit FreeRDP projekti kontrollimisest saab leida .
rdesktop
â tasuta RDP kliendi rakendus UNIX-pĂ”histe sĂŒsteemide jaoks. Seda saab kasutada ka Windowsis, kui projekti Cygwini all koostada. Litsentseeritud GPLv3 alusel.
See klient on saavutanud suure populaarsuse â seda kasutatakse vaikimisi ReactOSis ning sellele on saadaval ka kolmandate osapoolte graafilised front-endâid. Siiski on see ĂŒsna vana: esimene vĂ€ljalase toimus 4. aprillil 2001 â artikli kirjutamise hetkel on see 17 aastat vana.
Nagu juba mainitud, on projekt vÀga vÀike. See sisaldab umbes 30 tuhat rida koodi, mis on veidi kummaline, arvestades selle vanust. VÔrdluseks: FreeRDP sisaldab endas 320 tuhat rida. Siin on Cloci programmi vÀljund:

KĂ€imata kood
Tuvastatud on ulatuseta kood. VÔimalik, et viga on olemas. 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 ilmneb kohe funktsioonis main: nĂ€eme koodi, mis jĂ€rgneb operaatorile return â see fragment teostab mĂ€lu puhastust. Siiski ei kujuta viga ohtu: kogu eraldatud mĂ€lu puhastab operatsioonisĂŒsteem pĂ€rast programmi lĂ”petamist.
Vigade töötlemise puudumine
Massiivi alarĂŒnnak on vĂ”imalik. VÀÀrtus 'n' indeks vĂ”ib ulatuda -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);
}
....
}Selles koodifragmendis loetakse faili sisu puhvris seni, kuni fail on otsas. Siiski puudub siin vigade töötlemine: kui midagi lĂ€heb valesti, siis read tagastab -1, ja siis toimub massiivi piiride ĂŒletamine output.
EOF kasutamine char tĂŒĂŒbina
EOF ei tohiks vĂ”rrelda 'char' tĂŒĂŒbi vÀÀrtusega. '(c = fgetc(fp))' peaks olema 'int' tĂŒĂŒbist. 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 töötlemise lĂ”puni fail: kui fgetc tagastab sĂŒmboli, mille kood on 0xFF, siis see tĂ”lgendatakse lĂ”puks failina (EOF).
EOF see on konstant, mis on tavaliselt mÀÀratletud kui -1. NĂ€iteks koodimisvormis CP1251 on vene tĂ€hestiku viimane tĂ€ht koodiga 0xFF, mis vastab numbrile -1, kui me rÀÀgime tĂŒĂŒpi char. Tulemuseks on, et sĂŒmbol 0xFF, nagu ka EOF (-1) tĂ”lgendatakse kui lĂ”pp faili. Selliste vigade vĂ€ltimiseks tuleks funktsiooni tulemused hoida tĂŒĂŒpi fgetc muutujas 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 segas selle koodi autor || ja && tingimuses. Vaatame vÔimalikke vÀÀrtusi write_time ja change_time:
- MÔlemad muutujad on vÔrdsed 0: sel juhul satume haru muud: muutuja mod_time on alati 0, olenemata jÀrgnevast tingimusest.
- Ăks muutujatest on 0: mod_time on olema 0 (eeldusel, et teine muutuja on mitte-negatiivne), sest MIN valib kahest valikust vĂ€iksema.
- MÔlemad muutujad ei ole 0: valime minimaalne vÀÀrtus.
Kui tingimus asendada write_time && change_time kÀitumine nÀeb vÀlja korrektne:
- Ăks vĂ”i mĂ”lemad muutujad ei ole 0: valime mitte-null vÀÀrtuse.
- MÔlemad muutujad ei ole 0: valime minimaalne vÀÀrtus.
Fragment 2
Avaldis on alati tĂ”si. TĂ”enĂ€oliselt tuleks siin 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;
....
}Tundub, et siin on ka operaatorid segamini aetud || ja &&, vÔi == ja !=: muutuja ei saa samaaegselt vÔtta vÀÀrtust 20 ja 9.
Piiramatu stringi kopeerimine
Funktsiooni âsprintfâ kutsumine viib âfullpathâ puhvri ĂŒleujutamiseni. disk.c 1257
RD_NTSTATUS
disk_query_directory(....)
{
....
char *dirname, fullpath[PATH_MAX];
....
/* Saame teabe katalooge kandva faili kohta */
sprintf(fullpath, "%s/%s", dirname, pdirent->d_name);
....
}Funktsiooni tĂ€ielikku lĂ€bivaatamist arvesse vĂ”ttes on selge, et see kood ei tekita probleeme. Kuid tulevikus vĂ”ivad need tekkida: ĂŒks ettevaatamatu muudatus ja me saame puhvri ĂŒleujutamise â sprintf pole pole on piiratud, seega vĂ”ivad teede kokkupanemisel meie massiivi piiridest vĂ€lja minna. Soovitame seda kutset jĂ€lgida snprintf(fullpath, PATH_MAX, âŠ.).
Ăksikasjalik tingimus
Osa tingimuslikust vÀljendist 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 < 4 && add > 0)
{
....
}
}Kontrollimine add > 0 siin pole mÔtet: muutuja on alati suurem kui null, kuna read % 4 tagastab jÀÀgi jagamisest, kuid see ei ole kunagi vÔrdne 4-ga.
xrdp
â avatud lĂ€htekoodiga RDP serveri rakendus. Projekt on jagatud kaheks osaks:
- xrdp â protokolli rakendus. Jagatakse Apache 2.0 litsentsi alusel.
- xorgxrdp â Xorg draiverite komplekt, mida kasutatakse koos xrdp-ga. Litsents â X11 (nagu MIT, kuid keelab reklaamides kasutamise)
Projekti arendamine pĂ”hineb rdesktopi ja FreeRDP tulemustel. Alguses oli graafikaga töötamiseks vajalik kasutada eraldi VNC serverit vĂ”i spetsiaalset X11 serverit RDP toe jaoks â X11rdp, kuid xorgxrdp ilmumisega kadus vajadus nende jĂ€rele.
Selles artiklis me xorgxrdp-d ei kÀsitle.
Projekt xrdp, nagu eelmine, on vÀga vÀike ja sisaldab umbes 80 000 rida.

Veel trĂŒkivigu
Kood sisaldab sarnaste plokkide kogumit. Kontrollige elemente 'r', 'g', 'r' ridades 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 saadud librfxcodec raamatukogust, mis rakendab jpeg2000 koodeki RemoteFX tööks. Siit nÀib, et graafikandmed on segamini lÀinud - 'sinise' aja asemel salvestatakse 'punane'. Selline viga on tÔenÀoliselt tekkinud copy-paste'i tÔttu.
Sama probleem tabas ka sarnast funktsiooni rfx_encode_format_argb, mille teatas meile ka analĂŒsaator:
Kood sisaldab sarnaste plokkide kogumit. Kontrollige elemente 'a', 'r', 'g', 'r' ridades 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. Indeksi vÀÀrtus 'i - 8' vĂ”ib ulatuda kuni 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];
....
}
....
}Nende kahe faili massiivi deklareerimine ja mÀÀramine on mitteĂŒhtlane â suurus erineb ĂŒhe vĂ”rra. Kuid vigu ei esine â failis evdev-map.c on mÀÀratud Ă”ige suurus, seega pole piiridest vĂ€ljumist. Seega on see lihtsalt viga, mille lihtne parandada.
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 lugemine tĂŒĂŒbist unsigned short muutujasse tĂŒĂŒbist int. Kontroll siin ei ole vajalik, kuna loeme muutuja mittesisemise tĂŒĂŒbist ja mÀÀrame tulemuse suurema suurusega muutujasse, seega ei saa muutuja vĂ”tta negatiivset vÀÀrtust.
TĂŒhjad kontrollid
Osa tingimuslikust vÀljendusest 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: error");
return 1;
}
....
}Kasutatav vĂ”rdsus kontroll siin ei ole mĂ”ttekas, kuna meil on juba alguses vĂ”rdlemine. On tĂ€iesti tĂ”enĂ€oline, et see on trĂŒkiviga ja arendaja tahtis kasutada operaatorit || vale argumentide filtreerimiseks.
KokkuvÔte
Kontrollimisel ei leitud tĂ”siseid vigu, kuid mitmeid puudusi tuli siiski esile. Need projektid on kasutusel paljuski sĂŒsteemides, kuigi nende maht on vĂ€ike. VĂ€ikeses projekti ei pea tingimata olema palju vigu, seega ei tasu analĂŒsaatori tööd hinnata ainult vĂ€ikeste projektide pĂ”hjal. Rohkem teavet leiate artiklist ««.
VÔite alla laadida PVS-Studio prooviversiooni meie lehelt .
Kui soovite seda artiklit jagada ingliskeelsele publikule, palun kasutage tÔlke linki: Sergey Larin.
Allikas: habr.com
