RDP پروٹوکول کے ساتھ کام کرنے کے لیے اوپن سورس پروگراموں کی جانچ کے بارے میں مضامین کی ایک سیریز میں یہ دوسرا جائزہ ہے۔ اس میں ہم rdesktop کلائنٹ اور xrdp سرور کو دیکھیں گے۔
غلطیوں کی شناخت کے لیے ایک ٹول کے طور پر استعمال کیا جاتا ہے۔
مضمون میں صرف وہی غلطیاں پیش کی گئی ہیں جو مجھے دلچسپ لگیں۔ تاہم، منصوبے چھوٹے ہیں، اس لیے کچھ غلطیاں تھیں :)۔
نوٹ. FreeRDP پروجیکٹ کی تصدیق کے بارے میں ایک پچھلا مضمون پایا جا سکتا ہے۔
rdesktop
یہ کلائنٹ بہت مشہور ہے - یہ ReactOS میں بطور ڈیفالٹ استعمال ہوتا ہے، اور آپ اس کے لیے تھرڈ پارٹی گرافیکل فرنٹ اینڈز بھی تلاش کر سکتے ہیں۔ تاہم، وہ کافی بوڑھا ہے: اس کی پہلی ریلیز 4 اپریل 2001 کو ہوئی تھی - لکھنے کے وقت اس کی عمر 17 سال تھی۔
جیسا کہ میں نے پہلے ذکر کیا، یہ منصوبہ بہت چھوٹا ہے۔ اس میں کوڈ کی تقریباً 30 ہزار لائنیں ہیں، جو اس کی عمر کو دیکھتے ہوئے قدرے عجیب ہے۔ مقابلے کے لیے، FreeRDP 320 ہزار لائنوں پر مشتمل ہے۔ یہاں کلاک پروگرام کا آؤٹ پٹ ہے:
ناقابل رسائی کوڈ
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 کے برابر نہیں ہیں: کم از کم قدر منتخب کریں۔
کے ساتھ شرط کو تبدیل کرتے وقت write_time && change_time سلوک درست نظر آئے گا:
- ایک یا دونوں متغیرات 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 - پروٹوکول کا نفاذ۔ اپاچی 2.0 لائسنس کے تحت تقسیم کیا گیا۔
- xorgxrdp - xrdp کے ساتھ استعمال کے لیے Xorg ڈرائیوروں کا ایک سیٹ۔ لائسنس - X11 (جیسے MIT، لیکن اشتہارات میں استعمال ممنوع ہے)
پروجیکٹ کی ترقی rdesktop اور FreeRDP کے نتائج پر مبنی ہے۔ ابتدائی طور پر، گرافکس کے ساتھ کام کرنے کے لیے، آپ کو ایک علیحدہ VNC سرور، یا RDP سپورٹ کے ساتھ ایک خصوصی X11 سرور استعمال کرنا پڑتا تھا - X11rdp، لیکن 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++;
}
....
}
....
}
یہ کوڈ librfxcodec لائبریری سے لیا گیا تھا، جو RemoteFX کے لیے jpeg2000 کوڈیک کو لاگو کرتا ہے۔ یہاں، بظاہر، گرافک ڈیٹا چینلز کو ملایا گیا ہے - "نیلے" رنگ کے بجائے، "سرخ" ریکارڈ کیا گیا ہے۔ یہ غلطی غالباً کاپی پیسٹ کے نتیجے میں ظاہر ہوئی ہے۔
اسی طرح کے فنکشن میں بھی یہی مسئلہ پیش آیا 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 کا آزمائشی ورژن ڈاؤن لوڈ کر سکتے ہیں۔
اگر آپ انگریزی بولنے والے سامعین کے ساتھ اس مضمون کا اشتراک کرنا چاہتے ہیں، تو براہ کرم ترجمہ کا لنک استعمال کریں: Sergey Larin۔
ماخذ: www.habr.com