Ini adalah ulasan kedua dalam serangkaian artikel tentang memeriksa program open source untuk bekerja dengan protokol RDP. Di dalamnya, kita akan mempertimbangkan klien rdesktop dan server xrdp.
PVS-Studio digunakan sebagai alat untuk mendeteksi kesalahan. Ini adalah penganalisa kode statis untuk C, C ++, C #, dan Java, tersedia di Windows, Linux, dan macOS.
Artikel ini hanya menyajikan kesalahan-kesalahan yang tampak menarik bagi saya. Namun, proyeknya kecil, jadi ada beberapa kesalahan :).
Catatan Artikel sebelumnya tentang memeriksa proyek FreeRDP dapat ditemukan di
sini .
rdesktop
rdesktop adalah implementasi gratis klien RDP untuk sistem berbasis UNIX. Ini juga dapat digunakan di Windows, jika Anda membangun proyek di bawah Cygwin. Berlisensi berdasarkan GPLv3.
Klien ini sangat populer - digunakan secara default di ReactOS, Anda juga dapat menemukan ujung depan grafis pihak ketiga untuknya. Namun demikian, ia sudah cukup tua: rilis pertama terjadi pada 4 April 2001 - pada saat penulisan, usianya 17 tahun.
Seperti yang saya sebutkan sebelumnya, proyek ini sangat kecil. Ini berisi sekitar 30 ribu baris kode, yang agak aneh mengingat usianya. Sebagai perbandingan, FreeRDP berisi 320 ribu baris. Ini adalah output dari program Cloc:

Kode tidak dapat dijangkau
V779 Kode tidak terjangkau terdeteksi. Mungkin saja ada kesalahan. 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); }
Kesalahan menemui kita segera di fungsi
utama : kita melihat kode yang muncul setelah
pernyataan kembali - fragmen ini membersihkan memori. Namun demikian, kesalahan tidak menimbulkan ancaman: semua memori yang dialokasikan dibersihkan oleh sistem operasi setelah program berakhir.
Kurangnya penanganan kesalahan
V557 Array underrun dimungkinkan. Nilai indeks 'n' bisa mencapai -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';
Cuplikan kode dalam hal ini membaca dari file ke buffer hingga file berakhir. Namun, tidak ada penanganan kesalahan di sini: jika terjadi kesalahan, maka
baca akan mengembalikan -1, dan kemudian output array akan melampaui batas array.
Menggunakan EOF di char
V739 EOF tidak boleh dibandingkan dengan nilai tipe 'char'. '(C = fgetc (fp))' harus dari jenis '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++; } .... }
Di sini kita melihat pemrosesan salah mencapai akhir file: jika
fgetc mengembalikan karakter yang kodenya 0xFF, maka itu akan ditafsirkan sebagai akhir file (
EOF ).
EOF adalah konstanta, biasanya didefinisikan sebagai -1. Sebagai contoh, dalam pengkodean CP1251, huruf terakhir dari alfabet Rusia memiliki kode 0xFF, yang sesuai dengan angka -1, jika kita berbicara tentang variabel tipe
char . Ternyata karakter 0xFF, seperti
EOF (-1), dianggap sebagai akhir file. Untuk menghindari kesalahan seperti itu, hasil dari fungsi
fgetc harus disimpan dalam variabel
int .
Salah ketik
Fragmen 1Ekspresi
V547 'write_time' selalu salah. 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;
Mungkin pembuat kode ini bingung
|| dan
&& disediakan. Pertimbangkan opsi yang memungkinkan untuk
write_time dan
change_time :
- Kedua variabel tersebut adalah 0: dalam hal ini, kita sampai ke cabang else : variabel mod_time akan selalu 0 terlepas dari kondisi berikut.
- Salah satu variabel adalah 0: mod_time akan menjadi 0 (asalkan variabel lain memiliki nilai non-negatif), karena MIN akan memilih yang terkecil dari dua opsi.
- Kedua variabel tidak sama dengan 0: kami memilih nilai minimum.
Saat mengganti kondisi dengan
write_time && change_time, perilaku tersebut akan terlihat benar:
- Satu atau kedua variabel tidak sama dengan 0: pilih nilai bukan nol.
- Kedua variabel tidak sama dengan 0: kami memilih nilai minimum.
Fragmen 2Ekspresi
V547 selalu benar. Mungkin operator '&&' harus digunakan di sini. 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; .... }
Ternyata, operator
|| dan
&& , baik
== dan
! = : variabel tidak dapat mengambil nilai 20 dan 9 secara bersamaan.
Menyalin baris tanpa batas
V512 Panggilan fungsi 'sprintf' akan menyebabkan overflow buffer 'fullpath'. disk.c 1257
RD_NTSTATUS disk_query_directory(....) { .... char *dirname, fullpath[PATH_MAX]; .... sprintf(fullpath, "%s/%s", dirname, pdirent->d_name); .... }
Ketika mempertimbangkan fungsi, menjadi sangat jelas bahwa kode ini tidak menyebabkan masalah. Namun, mereka mungkin muncul di masa depan: satu perubahan ceroboh, dan kami mendapatkan buffer overflow -
sprintf tidak dibatasi oleh apa pun, jadi saat menyatukan jalur, kami bisa melampaui batas array. Dianjurkan untuk memperhatikan panggilan ini di
snprintf (fullpath, PATH_MAX, ....) .
Kondisi redundan
V560 Bagian dari ekspresi kondisional selalu benar: add> 0. scard.c 507
static void inRepos(STREAM in, unsigned int read) { SERVER_DWORD add = 4 - read % 4; if (add < 4 && add > 0) { .... } }
Memeriksa
add> 0 tidak berguna: variabel akan selalu lebih besar dari nol, karena
baca% 4 akan mengembalikan sisa divisi, dan itu tidak akan pernah menjadi 4.
xrdp
xrdp adalah implementasi server RDP open source. Proyek ini dibagi menjadi 2 bagian:
- xrdp - implementasi protokol. Didistribusikan di bawah lisensi Apache 2.0.
- xorgxrdp - Satu set driver Xorg untuk digunakan dengan xrdp. Lisensi - X11 (seperti MIT, tetapi melarang penggunaan dalam iklan)
Pengembangan proyek didasarkan pada hasil rdesktop dan FreeRDP. Awalnya, saya harus menggunakan server VNC terpisah untuk bekerja dengan grafik, atau server X11 khusus dengan dukungan RDP - X11rdp, tetapi dengan munculnya xorgxrdp, mereka tidak lagi diperlukan.
Pada artikel ini, kami tidak akan menyentuh xorgxrdp.
Proyek xrdp, seperti yang sebelumnya, sangat kecil dan berisi sekitar 80 ribu baris.

Kesalahan ketik lainnya
V525 Kode ini berisi kumpulan blok serupa. Periksa item 'r', 'g', 'r' pada baris 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;
Kode ini diambil dari pustaka librfxcodec yang mengimplementasikan codec jpeg2000 agar RemoteFX berfungsi. Di sini, tampaknya, saluran data grafis dicampur - alih-alih warna "biru", "merah" ditulis. Kesalahan seperti itu kemungkinan besar muncul sebagai hasil dari copy-paste.
Masalah yang sama jatuh ke fungsi yang serupa
rfx_encode_format_argb , yang analisanya juga memberi tahu kami tentang:
V525 Kode ini berisi kumpulan blok serupa. Periksa item 'a', 'r', 'g', 'r' pada baris 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++; }
Deklarasi susunan
V557 Array overrun dimungkinkan. Nilai indeks 'i - 8' bisa mencapai 129. genkeymap.c 142
Deklarasi dan definisi array dalam dua file ini tidak kompatibel - ukurannya berbeda dengan 1. Namun, tidak ada kesalahan terjadi - ukuran yang benar ditentukan dalam file evdev-map.c, jadi tidak ada jalan keluar dari batas. Jadi ini hanyalah cacat yang mudah diperbaiki.
Perbandingan tidak valid
V560 Bagian dari ekspresi kondisional selalu salah: (cap_len <0). xrdp_caps.c 616
Dalam suatu fungsi, variabel bertipe
unsigned short dibaca ke dalam variabel bertipe
int . Pemeriksaan tidak diperlukan di sini, karena kami membaca variabel dari tipe yang tidak ditandatangani dan menetapkan hasilnya ke variabel yang lebih besar, sehingga variabel tidak dapat mengambil nilai negatif.
Pemeriksaan yang tidak perlu
V560 Bagian dari ekspresi kondisional selalu benar: (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; } .... }
Cek ketidaksetaraan tidak masuk akal di sini, karena kami sudah memiliki perbandingan di awal. Kemungkinan ini adalah kesalahan ketik dan pengembang ingin menggunakan
|| untuk menyaring argumen yang tidak valid.
Kesimpulan
Selama inspeksi, tidak ada kesalahan serius yang terdeteksi, tetapi banyak kekurangan ditemukan. Namun, proyek-proyek ini digunakan dalam banyak sistem, walaupun dalam ruang lingkup kecil. Sebuah proyek kecil tidak harus memiliki banyak kesalahan, jadi Anda tidak boleh menilai pekerjaan penganalisa hanya pada proyek-proyek kecil. Anda dapat membaca lebih lanjut tentang ini di artikel "
Sensasi, yang dikonfirmasi oleh angka ."
Anda dapat mengunduh versi percobaan PVS-Studio di
situs web kami.

Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Sergey Larin.
Memeriksa rdesktop dan xrdp dengan PVS-Studio