
Huu ni uhakiki wa pili katika mfululizo wa makala kuhusu kujaribu programu huria za kufanya kazi na itifaki ya RDP. Ndani yake tutaangalia mteja wa rdesktop na seva ya xrdp.
Inatumika kama zana ya kutambua makosa Ni kichambuzi cha msimbo tuli cha C, C++, C# na Java, kinachopatikana kwenye mifumo Windows, Linux и macOS.
Makala hiyo inatoa tu makosa ambayo yalionekana kunivutia. Hata hivyo, miradi ni ndogo, kwa hiyo kulikuwa na makosa machache :).
Kumbuka. Nakala iliyotangulia kuhusu kuangalia mradi wa FreeRDP inaweza kupatikana .
rdesktop
— свободная реализация клиента RDP для UNIX-based систем. Его также можно использовать и под Windows, если собирать проект под Cygwin. Лицензирован под GPLv3.
Mteja huyu ni maarufu sana - hutumiwa na chaguo-msingi katika ReactOS, na unaweza pia kupata sehemu za mbele za picha za wahusika wengine. Walakini, yeye ni mzee kabisa: kutolewa kwake kwa kwanza kulifanyika Aprili 4, 2001 - wakati wa kuandika, ana umri wa miaka 17.
Kama nilivyosema hapo awali, mradi huo ni mdogo sana. Ina takriban mistari elfu 30 ya msimbo, ambayo ni ya kushangaza kidogo ukizingatia umri wake. Kwa kulinganisha, FreeRDP ina mistari 320 elfu. Hapa kuna matokeo ya mpango wa Cloc:

Msimbo usioweza kufikiwa
Nambari ya kuthibitisha isiyopatikana imegunduliwa. Inawezekana kuwa kuna kosa. 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);
}Hitilafu inatukuta mara moja kwenye chaguo la kukokotoa kuu: tunaona msimbo unakuja baada ya opereta kurudi - kipande hiki hufanya kusafisha kumbukumbu. Hata hivyo, hitilafu haitoi tishio: kumbukumbu zote zilizotengwa zitafutwa na mfumo wa uendeshaji baada ya programu kuondoka.
Hakuna kushughulikia makosa
Kushindwa kwa safu kunawezekana. Thamani ya faharasa ya 'n' inaweza kufikia -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);
}
....
}Kijisehemu cha msimbo katika kesi hii kinasoma kutoka kwa faili hadi kwenye bafa hadi faili iishe. Walakini, hakuna kushughulikia makosa hapa: ikiwa kitu kitaenda vibaya, basi kusoma itarudi -1, na kisha safu itazidiwa pato.
Kutumia EOF katika aina ya char
EOF haipaswi kulinganishwa na thamani ya aina ya 'char'. '(c = fgetc(fp))' inapaswa kuwa ya aina ya '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++;
}
....
}Hapa tunaona utunzaji usio sahihi wa kufikia mwisho wa faili: ikiwa fgetc inarudisha herufi ambayo nambari yake ni 0xFF, itatafsiriwa kama mwisho wa faili (EOF).
EOF ni mara kwa mara, kwa kawaida hufafanuliwa kama -1. Kwa mfano, katika usimbuaji wa CP1251, herufi ya mwisho ya alfabeti ya Kirusi ina nambari 0xFF, ambayo inalingana na nambari -1 ikiwa tunazungumza juu ya kutofautisha kama. char. Inageuka kuwa ishara 0xFF, kama EOF (-1) inafasiriwa kama mwisho wa faili. Ili kuepuka makosa hayo, matokeo ya kazi ni fgetc inapaswa kuhifadhiwa katika tofauti kama int.
Typos
Sehemu ya 1
Usemi 'andika_wakati' huwa si kweli. 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; // <=
....
}Labda mwandishi wa nambari hii alikosea || и && katika hali. Wacha tuangalie chaguzi zinazowezekana za maadili kuandika_muda и mabadiliko_wakati:
- Vigezo vyote viwili ni sawa na 0: katika kesi hii tutaishia kwenye tawi mwingine: kutofautiana mod_time daima itakuwa 0 bila kujali hali inayofuata.
- Moja ya vigezo ni 0: mod_time itakuwa sawa na 0 (mradi tofauti nyingine ina thamani isiyo hasi), kwa sababu MIN itachagua ndogo kati ya chaguzi hizo mbili.
- Vigezo vyote viwili si sawa na 0: chagua thamani ya chini.
Wakati wa kubadilisha hali na kuandika_muda && change_time tabia itaonekana kuwa sawa:
- Kigezo kimoja au vyote viwili si sawa na 0: chagua thamani isiyo ya sifuri.
- Vigezo vyote viwili si sawa na 0: chagua thamani ya chini.
Sehemu ya 2
Kujieleza siku zote ni kweli. Pengine opereta ya '&&' inafaa kutumika hapa. 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;
....
}Inavyoonekana waendeshaji wamechanganyika hapa pia || и &&Au == и !=: Tofauti haiwezi kuwa na thamani 20 na 9 kwa wakati mmoja.
Kunakili bila kikomo kwa mstari
Simu ya chaguo za kukokotoa za 'sprintf' itasababisha kufurika kwa 'njia kamili' ya bafa. disk.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);
....
}Unapoangalia kazi kwa ukamilifu, itakuwa wazi kuwa kanuni hii haina kusababisha matatizo. Walakini, zinaweza kutokea katika siku zijazo: badiliko moja la kutojali na tutapata kufurika kwa buffer - mbio mbio haijazuiliwa na chochote, kwa hivyo tunapounganisha njia tunaweza kwenda zaidi ya mipaka ya safu. Inashauriwa kutambua simu hii snprintf(njia kamili, PATH_MAX, ....).
Hali isiyo ya lazima
Sehemu ya usemi wa masharti ni kweli kila wakati: ongeza > 0. scard.c 507
static void
inRepos(STREAM in, unsigned int read)
{
SERVER_DWORD add = 4 - read % 4;
if (add < 4 && add > 0)
{
....
}
}Проверка ongeza > 0 hakuna haja hapa: kutofautisha daima kuwa kubwa kuliko sifuri, kwa sababu soma %4 itarudisha sehemu iliyobaki, lakini haitakuwa sawa na 4.
xrdp
- Utekelezaji wa seva ya RDP yenye msimbo wa chanzo huria. Mradi umegawanywa katika sehemu 2:
- xrdp - utekelezaji wa itifaki. Inasambazwa chini ya leseni ya Apache 2.0.
- xorgxrdp - Seti ya viendeshi vya Xorg kwa matumizi na xrdp. Leseni - X11 (kama MIT, lakini inakataza matumizi katika utangazaji)
Uendelezaji wa mradi unategemea matokeo ya rdesktop na FreeRDP. Hapo awali, ili kufanya kazi na michoro, ilibidi utumie seva tofauti ya VNC, au seva maalum ya X11 iliyo na msaada wa RDP - X11rdp, lakini kwa ujio wa xorgxrdp, hitaji lao lilitoweka.
Katika nakala hii hatutashughulikia xorgxrdp.
Mradi wa xrdp, kama ule uliopita, ni mdogo sana na una takriban mistari elfu 80.

Makosa zaidi
Msimbo una mkusanyiko wa vitalu sawa. Angalia vipengee 'r', 'g', 'r' katika mistari ya 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++;
}
....
}
....
}Msimbo huu ulichukuliwa kutoka kwa maktaba ya librfxcodec, ambayo hutumia kodeki ya jpeg2000 ya RemoteFX. Hapa, inaonekana, njia za data za picha zimechanganywa - badala ya rangi ya "bluu", "nyekundu" imerekodiwa. Hitilafu hii ina uwezekano mkubwa ilionekana kama matokeo ya kunakili-kubandika.
Tatizo sawa lilitokea katika utendaji sawa rfx_encode_format_argb, ambayo mchambuzi pia alituambia:
Msimbo una mkusanyiko wa vitalu sawa. Angalia vipengee 'a', 'r', 'g', 'r' katika mistari ya 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++;
}Tamko la safu
Kupanda kwa safu kunawezekana. Thamani ya faharasa ya 'i - 8' inaweza kufikia 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];
....
}
....
}Tamko na ufafanuzi wa safu katika faili hizi mbili haziendani - ukubwa hutofautiana na 1. Hata hivyo, hakuna makosa yanayotokea - ukubwa sahihi umeelezwa kwenye faili ya evdev-map.c, kwa hiyo hakuna nje ya mipaka. Kwa hivyo hii ni mdudu tu ambayo inaweza kusasishwa kwa urahisi.
Ulinganisho usio sahihi
Sehemu ya usemi wa masharti huwa sio kweli: (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))
{
....
}
....
}Chaguo za kukokotoa husoma kigeu cha aina fupi isiyosainiwa katika variable kama int. Kuangalia hakuhitajiki hapa kwa sababu tunasoma kigezo ambacho hakijatiwa saini na kugawa matokeo kwa kigezo kikubwa zaidi, kwa hivyo kigezo hakiwezi kuchukua thamani hasi.
Hundi zisizo za lazima
Sehemu ya usemi wa masharti ni kweli kila wakati: (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;
}
....
}Ukaguzi wa ukosefu wa usawa hauna maana hapa kwa kuwa tayari tuna ulinganisho mwanzoni. Kuna uwezekano kwamba hii ni typo na msanidi alitaka kutumia opereta || kuchuja hoja zisizo sahihi.
Hitimisho
Wakati wa ukaguzi, hakuna makosa makubwa yaliyotambuliwa, lakini mapungufu mengi yalipatikana. Hata hivyo, miundo hii hutumiwa katika mifumo mingi, ingawa ni ndogo katika upeo. Mradi mdogo sio lazima uwe na makosa mengi, kwa hivyo hupaswi kuhukumu utendaji wa analyzer tu kwenye miradi ndogo. Unaweza kusoma zaidi juu ya hii katika makala "".
Unaweza kupakua toleo la majaribio la PVS-Studio kutoka kwetu .
Ikiwa ungependa kushiriki makala hii na hadhira inayozungumza Kiingereza, tafadhali tumia kiungo cha kutafsiri: Sergey Larin.
Chanzo: mapenzi.com
