RDP પ્રોટોકોલ સાથે કામ કરવા માટે ઓપન સોર્સ પ્રોગ્રામ્સનું પરીક્ષણ કરવા વિશેના લેખોની શ્રેણીમાં આ બીજી સમીક્ષા છે. તેમાં આપણે rdesktop ક્લાયન્ટ અને xrdp સર્વરને જોઈશું.
ભૂલોને ઓળખવા માટેના સાધન તરીકે ઉપયોગ થાય છે
લેખ ફક્ત તે ભૂલો રજૂ કરે છે જે મને રસપ્રદ લાગતી હતી. જો કે, પ્રોજેક્ટ નાના છે, તેથી થોડી ભૂલો હતી :).
નોંધણી. ફ્રીઆરડીપી પ્રોજેક્ટ વેરિફિકેશન વિશે અગાઉનો લેખ મળી શકે છે
rdesktop
આ ક્લાયંટ ખૂબ જ લોકપ્રિય છે - તે ReactOS માં ડિફૉલ્ટ રૂપે ઉપયોગમાં લેવાય છે, અને તમે તેના માટે તૃતીય-પક્ષ ગ્રાફિકલ ફ્રન્ટ-એન્ડ્સ પણ શોધી શકો છો. જો કે, તે ખૂબ વૃદ્ધ છે: તેની પ્રથમ રજૂઆત 4 એપ્રિલ, 2001 ના રોજ થઈ હતી - લેખન સમયે, તે 17 વર્ષનો છે.
મેં અગાઉ નોંધ્યું છે તેમ, પ્રોજેક્ટ તદ્દન નાનો છે. તેમાં કોડની લગભગ 30 હજાર લાઇન છે, જે તેની ઉંમરને ધ્યાનમાં રાખીને થોડી વિચિત્ર છે. સરખામણી માટે, ફ્રીઆરડીપીમાં 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++;
}
....
}
અહીં આપણે ફાઇલના અંત સુધી પહોંચવાનું ખોટું હેન્ડલિંગ જોયું છે: જો fgetc એક અક્ષર પરત કરે છે જેનો કોડ 0xFF છે, તેને ફાઇલના અંત તરીકે અર્થઘટન કરવામાં આવશે (EOF).
EOF તે સ્થિર છે, સામાન્ય રીતે -1 તરીકે વ્યાખ્યાયિત થાય છે. ઉદાહરણ તરીકે, CP1251 એન્કોડિંગમાં, રશિયન મૂળાક્ષરોના છેલ્લા અક્ષરમાં કોડ 0xFF છે, જે નંબર -1 ને અનુરૂપ છે જો આપણે ચલ વિશે વાત કરી રહ્યા છીએ ઘરનાં પરચૂરણ કામો. તે તારણ આપે છે કે પ્રતીક 0xFF, જેમ EOF (-1) ફાઇલના અંત તરીકે અર્થઘટન કરવામાં આવે છે. આવી ભૂલોને ટાળવા માટે, કાર્યનું પરિણામ છે fgetc જેવા ચલમાં સંગ્રહિત હોવું જોઈએ પૂર્ણાંક.
ટાઈપો
ટુકડો 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 ની બરાબર છે: આ કિસ્સામાં આપણે એક શાખામાં સમાપ્ત થઈશું બીજું: ચલ મોડ_સમય અનુગામી સ્થિતિને ધ્યાનમાં લીધા વિના હંમેશા 0 રહેશે.
- ચલોમાંનું એક 0 છે: મોડ_સમય 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++;
}
....
}
....
}
આ કોડ 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 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