Энэ бол RDP протоколтой ажиллах нээлттэй эхийн програмуудыг турших тухай цуврал нийтлэлийн хоёр дахь тойм юм. Үүн дээр бид rdesktop клиент болон xrdp серверийг харах болно.
Алдааг тодорхойлох хэрэгсэл болгон ашигладаг
Нийтлэлд зөвхөн надад сонирхолтой санагдсан алдаануудыг л харуулав. Гэхдээ төслүүд нь жижиг учраас алдаа багатай байсан :).
тайлбар. FreeRDP төслийн баталгаажуулалтын талаарх өмнөх нийтлэлийг олж болно
ширээний компьютер
Энэ үйлчлүүлэгч нь маш их алдартай бөгөөд үүнийг ReactOS-д анхдагч байдлаар ашигладаг бөгөөд та үүнд зориулж гуравдагч этгээдийн график дүрсийг олох боломжтой. Гэсэн хэдий ч тэр нэлээд хөгширсөн: түүний анхны хувилбар 4 оны 2001-р сарын 17-нд болсон - бичиж байх үед тэр XNUMX настай.
Миний өмнө дурдсанчлан, төсөл нь маш бага юм. Энэ нь ойролцоогоор 30 мянган мөр код агуулдаг бөгөөд энэ нь наснаасаа харахад хачирхалтай юм. Харьцуулбал, FreeRDP нь 320 мянган мөрийг агуулдаг. Cloc програмын гаралтыг энд харуулав.
Хүрэх боломжгүй код
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);
}
Алдаа нь функц дээр бидэнтэй шууд тулгардаг үндсэн: бид операторын араас ирж буй кодыг харж байна буцах - энэ хэсэг нь санах ойн цэвэрлэгээ хийдэг. Гэсэн хэдий ч алдаа нь аюул занал учруулахгүй: програмаас гарсны дараа бүх хуваарилагдсан санах ойг үйлдлийн системээр цэвэрлэх болно.
Алдаатай ажиллахгүй
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);
}
....
}
Энэ тохиолдолд кодын хэсэг нь файл дуусах хүртэл файлаас буфер руу уншина. Гэсэн хэдий ч энд ямар ч алдаа байхгүй: хэрэв ямар нэг зүйл буруу болвол унш -1 буцаж ирэх ба дараа нь массив хэтрэнэ Гаралт.
Тэмдэгтийн төрөлд EOF ашиглах
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++;
}
....
}
Энд бид файлын төгсгөлд хүрэхийн тулд буруу харьцаж байгааг харж байна: if fgetc код нь 0xFF тэмдэгтийг буцаавал энэ нь файлын төгсгөл гэж ойлгогдоно (EOF).
EOF Энэ нь тогтмол бөгөөд ихэвчлэн -1 гэж тодорхойлогддог. Жишээлбэл, CP1251 кодчилолд орос цагаан толгойн сүүлчийн үсэг нь 0xFF кодтой бөгөөд хэрэв бид хувьсагчийн тухай ярьж байгаа бол -1 тоотой тохирч байна. Хорхой. Энэ нь 0xFF тэмдэгт шиг харагдаж байна EOF (-1) нь файлын төгсгөл гэж ойлгогдоно. Иймэрхүү алдаанаас зайлсхийхийн тулд функцийн үр дүн нь байна fgetc гэх мэт хувьсагчид хадгалагдах ёстой INT.
Бичгийн алдаа
Хэсэг 1
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; // <=
....
}
Магадгүй энэ кодыг зохиогч буруу ойлгосон байх || и && нөхцөлд. Үнэт зүйлийн боломжит хувилбаруудыг авч үзье бичих_цаг и өөрчлөх_цаг:
- Хоёр хувьсагч хоёулаа 0-тэй тэнцүү байна: энэ тохиолдолд бид салбар руу орох болно бас: хувьсагч mod_time Дараагийн нөхцөл байдлаас үл хамааран үргэлж 0 байх болно.
- Хувьсагчдын нэг нь 0: mod_time 0-тэй тэнцүү байх болно (нөгөө хувьсагч нь сөрөг бус утгатай байх тохиолдолд), учир нь MIN хоёр сонголтоос багаыг нь сонгоно.
- Хоёр хувьсагч хоёулаа 0-тэй тэнцүү биш: хамгийн бага утгыг сонгоно уу.
Нөхцөл байдлыг солих үед бичих_цаг &&хугацаа өөрчлөх зан байдал зөв харагдах болно:
- Нэг буюу хоёр хувьсагч 0-тэй тэнцүү биш байна: тэгээс өөр утгыг сонгоно уу.
- Хоёр хувьсагч хоёулаа 0-тэй тэнцүү биш: хамгийн бага утгыг сонгоно уу.
Хэсэг 2
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;
....
}
Энд операторууд ч холилдсон бололтой || и &&, эсвэл == и !=: Хувьсагч нь нэгэн зэрэг 20 ба 9 гэсэн утгатай байж болохгүй.
Хязгааргүй мөр хуулбарлах
RD_NTSTATUS
disk_query_directory(....)
{
....
char *dirname, fullpath[PATH_MAX];
....
/* Get information for directory entry */
sprintf(fullpath, "%s/%s", dirname, pdirent->d_name);
....
}
Функцийг бүрэн эхээр нь харахад энэ код нь асуудал үүсгэдэггүй нь тодорхой болно. Гэсэн хэдий ч тэд ирээдүйд гарч ирж магадгүй юм: нэг хайхрамжгүй өөрчлөлт, бид буфер халих болно - спринт юугаар ч хязгаарлагдахгүй, тиймээс замыг холбохдоо бид массивын хил хязгаараас давж гарах боломжтой. Энэ дуудлагыг анхаарч үзэхийг зөвлөж байна snprintf(бүтэн зам, PATH_MAX, ….).
Илүүдэл нөхцөл
static void
inRepos(STREAM in, unsigned int read)
{
SERVER_DWORD add = 4 - read % 4;
if (add < 4 && add > 0)
{
....
}
}
хяналт нэмэх > 0 энд шаардлагагүй: хувьсагч үргэлж тэгээс их байх болно, учир нь унших % 4 хуваагдлын үлдэгдлийг буцаана, гэхдээ энэ нь хэзээ ч 4-тэй тэнцэхгүй.
xrdp
- xrdp - протоколын хэрэгжилт. Apache 2.0 лицензийн дагуу түгээгдсэн.
- xorgxrdp - xrdp-д ашиглах Xorg драйверуудын багц. Лиценз - X11 (MIT шиг, гэхдээ сурталчилгаанд ашиглахыг хориглодог)
Төслийг боловсруулах нь rdesktop болон FreeRDP-ийн үр дүнд тулгуурладаг. Графиктай ажиллахын тулд та тусдаа VNC сервер эсвэл RDP дэмжлэгтэй X11rdp тусгай X11 сервер ашиглах шаардлагатай байсан боловч xorgxrdp гарч ирснээр тэдний хэрэгцээ алга болсон.
Энэ нийтлэлд бид xorgxrdp-г хамрахгүй.
xrdp төсөл нь өмнөхтэй адил маш жижиг бөгөөд ойролцоогоор 80 мянган мөрийг агуулдаг.
Илүү олон үсгийн алдаа
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++;
}
....
}
....
}
Энэ кодыг RemoteFX-д зориулсан jpeg2000 кодлогчийг хэрэгжүүлдэг librfxcodec номын сангаас авсан болно. Энд график мэдээллийн сувгууд холилдсон бололтой - "цэнхэр" өнгөний оронд "улаан" тэмдэглэгдсэн байна. Энэ алдаа нь хуулж буулгасны үр дүнд гарсан байх магадлалтай.
Үүнтэй ижил төстэй функцэд ижил асуудал гарсан rfx_encode_format_argb, үүнийг анализатор бидэнд хэлсэн:
while (x < 64)
{
*la_buf++ = a;
*lr_buf++ = r;
*lg_buf++ = g;
*lb_buf++ = r;
x++;
}
Массивын мэдэгдэл
// 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];
....
}
....
}
Эдгээр хоёр файл дахь массивын мэдэгдэл болон тодорхойлолт нь хоорондоо таарахгүй байна - хэмжээ нь 1-ээр ялгаатай байна. Гэсэн хэдий ч алдаа гарахгүй - evdev-map.c файлд зөв хэмжээг зааж өгсөн тул хил хязгаар байхгүй. Тиймээс энэ бол зүгээр л амархан засч болох алдаа юм.
Буруу харьцуулалт
// 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))
{
....
}
....
}
Функц нь төрлийн хувьсагчийг уншдаг гарын үсэггүй богино гэх мэт хувьсагч болж хувирна INT. Энд шалгах шаардлагагүй, учир нь бид тэмдэггүй хувьсагчийг уншиж, үр дүнг том хувьсагч руу оноож байгаа тул хувьсагч сөрөг утгыг авч чадахгүй.
Шаардлагагүй шалгалт
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;
}
....
}
Энд тэгш бус байдлын шалгалт нь утгагүй юм, учир нь бид аль хэдийн харьцуулалт хийсэн. Энэ нь үсгийн алдаа бөгөөд хөгжүүлэгч операторыг ашиглахыг хүссэн байх магадлалтай || хүчингүй аргументуудыг шүүх.
дүгнэлт
Шалгалтын явцад ноцтой алдаа дутагдал илрээгүй ч олон дутагдал илэрсэн. Гэсэн хэдий ч эдгээр загварууд нь цар хүрээний хувьд бага ч гэсэн олон системд ашиглагддаг. Жижиг төсөл нь олон алдаатай байх албагүй тул та зөвхөн жижиг төслүүд дээр анализаторын ажиллагааг шүүж болохгүй. Та энэ талаар дэлгэрэнгүйг " нийтлэлээс уншиж болно.
Та биднээс PVS-Studio-ийн туршилтын хувилбарыг татаж авах боломжтой
Хэрэв та энэ нийтлэлийг англи хэлээр ярьдаг үзэгчидтэй хуваалцахыг хүсвэл орчуулгын холбоосыг ашиглана уу: Сергей Ларин.
Эх сурвалж: www.habr.com