
RDP ప్రోటోకాల్తో పని చేయడానికి ఓపెన్ సోర్స్ ప్రోగ్రామ్లను పరీక్షించడం గురించి కథనాల శ్రేణిలో ఇది రెండవ సమీక్ష. అందులో మనం rdesktop క్లైంట్ మరియు xrdp సర్వర్ని చూస్తాము.
లోపాలను గుర్తించడానికి సాధనంగా ఉపయోగించబడుతుంది ఇది C, C++, C# మరియు జావా కోసం ప్లాట్ఫారమ్లలో లభించే ఒక స్టాటిక్ కోడ్ అనలైజర్. Windows, Linux и macOS.
వ్యాసం నాకు ఆసక్తికరంగా అనిపించిన లోపాలను మాత్రమే అందిస్తుంది. అయితే, ప్రాజెక్టులు చిన్నవి, కాబట్టి కొన్ని తప్పులు ఉన్నాయి :).
వ్యాఖ్య. FreeRDP ప్రాజెక్ట్ ధృవీకరణ గురించి మునుపటి కథనాన్ని కనుగొనవచ్చు .
rdesktop
— UNIX-ఆధారిత సిస్టమ్ల కోసం RDP క్లయింట్ యొక్క ఉచిత అమలు. దీనిని కింద కూడా ఉపయోగించవచ్చు Windows, మీరు ప్రాజెక్ట్ను Cygwin కింద నిర్మిస్తే. GPLv3 కింద లైసెన్స్ చేయబడింది.
ఈ క్లయింట్ చాలా ప్రజాదరణ పొందింది - ఇది ReactOSలో డిఫాల్ట్గా ఉపయోగించబడుతుంది మరియు మీరు దాని కోసం మూడవ పక్ష గ్రాఫికల్ ఫ్రంట్-ఎండ్లను కూడా కనుగొనవచ్చు. అయినప్పటికీ, అతను చాలా పాతవాడు: అతని మొదటి విడుదల ఏప్రిల్ 4, 2001 న జరిగింది - వ్రాసే సమయంలో, అతని వయస్సు 17 సంవత్సరాలు.
నేను ముందే గుర్తించినట్లుగా, ప్రాజెక్ట్ చాలా చిన్నది. ఇది సుమారు 30 వేల పంక్తుల కోడ్ను కలిగి ఉంది, ఇది దాని వయస్సును పరిగణనలోకి తీసుకుంటే కొంచెం వింతగా ఉంటుంది. పోలిక కోసం, FreeRDP 320 వేల లైన్లను కలిగి ఉంది. క్లోక్ ప్రోగ్రామ్ యొక్క అవుట్పుట్ ఇక్కడ ఉంది:

చేరుకోలేని కోడ్
అందుబాటులో లేని కోడ్ కనుగొనబడింది. లోపం ఉన్న అవకాశం ఉంది. 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);
}లోపం ఫంక్షన్లో వెంటనే మాకు ఎదురవుతుంది ప్రధాన: ఆపరేటర్ తర్వాత కోడ్ రావడం మనకు కనిపిస్తుంది తిరిగి - ఈ భాగం మెమరీ క్లీనింగ్ చేస్తుంది. అయినప్పటికీ, లోపం ముప్పును కలిగించదు: ప్రోగ్రామ్ నుండి నిష్క్రమించిన తర్వాత అన్ని కేటాయించిన మెమరీ ఆపరేటింగ్ సిస్టమ్ ద్వారా క్లియర్ చేయబడుతుంది.
లోపం నిర్వహణ లేదు
అర్రే అండర్ రన్ సాధ్యమే. 'n' సూచిక విలువ -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);
}
....
}ఈ సందర్భంలో కోడ్ స్నిప్పెట్ ఫైల్ ముగిసే వరకు ఫైల్ నుండి బఫర్లోకి చదవబడుతుంది. అయితే, ఇక్కడ లోపం నిర్వహణ లేదు: ఏదైనా తప్పు జరిగితే, అప్పుడు చదవండి -1 తిరిగి వస్తుంది, ఆపై శ్రేణి ఓవర్రన్ అవుతుంది అవుట్పుట్.
చార్ టైప్లో EOFని ఉపయోగించడం
EOFని 'చార్' రకం విలువతో పోల్చకూడదు. '(c = fgetc(fp))' '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++;
}
....
}ఫైల్ ముగింపుకు చేరుకోవడంలో తప్పు నిర్వహణను ఇక్కడ చూస్తాము: if fgetc 0xFF కోడ్ ఉన్న అక్షరాన్ని అందిస్తుంది, అది ఫైల్ ముగింపుగా వివరించబడుతుంది (EOF).
EOF ఇది స్థిరంగా ఉంటుంది, సాధారణంగా -1గా నిర్వచించబడుతుంది. ఉదాహరణకు, CP1251 ఎన్కోడింగ్లో, రష్యన్ వర్ణమాల యొక్క చివరి అక్షరం 0xFF కోడ్ను కలిగి ఉంటుంది, ఇది మనం వేరియబుల్ గురించి మాట్లాడుతుంటే -1 సంఖ్యకు అనుగుణంగా ఉంటుంది. చార్. ఇది 0xFF చిహ్నం వలె మారుతుంది EOF (-1) ఫైల్ ముగింపుగా వివరించబడింది. అటువంటి లోపాలను నివారించడానికి, ఫంక్షన్ యొక్క ఫలితం fgetc వంటి వేరియబుల్లో నిల్వ చేయాలి పూర్ణాంకానికి.
అక్షర దోషాలు
శకలం 1
'write_time' అనే వ్యక్తీకరణ ఎల్లప్పుడూ తప్పు. 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; // <=
....
}బహుశా ఈ కోడ్ రచయిత తప్పుగా భావించి ఉండవచ్చు || и && పరిస్థితిలో. విలువల కోసం సాధ్యమయ్యే ఎంపికలను పరిశీలిద్దాం వ్రాసే_సమయం и మార్పు_సమయం:
- రెండు వేరియబుల్స్ 0 కి సమానం: ఈ సందర్భంలో మనం ఒక శాఖలో ముగుస్తుంది వేరే: వేరియబుల్ మోడ్_టైమ్ తదుపరి పరిస్థితితో సంబంధం లేకుండా ఎల్లప్పుడూ 0 ఉంటుంది.
- వేరియబుల్స్లో ఒకటి 0: మోడ్_టైమ్ 0కి సమానంగా ఉంటుంది (ఇతర వేరియబుల్ నాన్-నెగటివ్ విలువను కలిగి ఉంటే), ఎందుకంటే MIN రెండు ఎంపికలలో చిన్నదాన్ని ఎంచుకుంటుంది.
- రెండు వేరియబుల్స్ 0కి సమానం కాదు: కనీస విలువను ఎంచుకోండి.
పరిస్థితిని భర్తీ చేసినప్పుడు వ్రాసే_సమయం && సమయాన్ని_మార్చు ప్రవర్తన సరిగ్గా కనిపిస్తుంది:
- ఒకటి లేదా రెండు వేరియబుల్స్ 0కి సమానం కాదు: సున్నా కాని విలువను ఎంచుకోండి.
- రెండు వేరియబుల్స్ 0కి సమానం కాదు: కనీస విలువను ఎంచుకోండి.
శకలం 2
వ్యక్తీకరణ ఎల్లప్పుడూ నిజం. బహుశా ఇక్కడ '&&' ఆపరేటర్ని ఉపయోగించాలి. 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;
....
}ఇక్కడ ఆపరేటర్లు కూడా కలగజేసుకున్నట్లు తెలుస్తోంది || и &&, లేదా == и !=: ఒక వేరియబుల్ ఒకే సమయంలో 20 మరియు 9 విలువను కలిగి ఉండకూడదు.
అపరిమిత లైన్ కాపీయింగ్
'sprintf' ఫంక్షన్ యొక్క కాల్ బఫర్ 'ఫుల్పాత్' ఓవర్ఫ్లోకి దారి తీస్తుంది. 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);
....
}మీరు ఫంక్షన్ను పూర్తిగా చూసినప్పుడు, ఈ కోడ్ సమస్యలను కలిగించదని స్పష్టమవుతుంది. అయినప్పటికీ, అవి భవిష్యత్తులో తలెత్తవచ్చు: ఒక అజాగ్రత్త మార్పు మరియు మేము బఫర్ ఓవర్ఫ్లో పొందుతాము - స్ప్రింట్ఎఫ్ దేనికీ పరిమితం కాదు, కాబట్టి మార్గాలను కలుపుతున్నప్పుడు మేము శ్రేణి యొక్క సరిహద్దులను దాటి వెళ్ళవచ్చు. ఈ కాల్ని గమనించాలని సిఫార్సు చేయబడింది snprintf(ఫుల్పాత్, PATH_MAX, ....).
అనవసరమైన పరిస్థితి
షరతులతో కూడిన వ్యక్తీకరణలో కొంత భాగం ఎల్లప్పుడూ నిజం: జోడించు > 0. scard.c 507
static void
inRepos(STREAM in, unsigned int read)
{
SERVER_DWORD add = 4 - read % 4;
if (add < 4 && add > 0)
{
....
}
}ఇన్స్పెక్షన్ జోడించు > 0 ఇక్కడ అవసరం లేదు: వేరియబుల్ ఎల్లప్పుడూ సున్నా కంటే ఎక్కువగా ఉంటుంది, ఎందుకంటే % 4 చదవండి విభజన యొక్క మిగిలిన భాగాన్ని తిరిగి ఇస్తుంది, కానీ అది ఎప్పటికీ 4కి సమానంగా ఉండదు.
xrdp
— ఓపెన్ సోర్స్ కోడ్తో RDP సర్వర్ అమలు. ప్రాజెక్ట్ 2 భాగాలుగా విభజించబడింది:
- xrdp - ప్రోటోకాల్ అమలు. Apache 2.0 లైసెన్స్ క్రింద పంపిణీ చేయబడింది.
- xorgxrdp - xrdpతో ఉపయోగించడానికి Xorg డ్రైవర్ల సమితి. లైసెన్స్ - X11 (MIT లాగా, కానీ ప్రకటనలలో ఉపయోగించడాన్ని నిషేధిస్తుంది)
ప్రాజెక్ట్ అభివృద్ధి rdesktop మరియు FreeRDP ఫలితాలపై ఆధారపడి ఉంటుంది. ప్రారంభంలో, గ్రాఫిక్స్తో పని చేయడానికి, మీరు ప్రత్యేక VNC సర్వర్ లేదా RDP మద్దతుతో ప్రత్యేక X11 సర్వర్ను ఉపయోగించాల్సి వచ్చింది - X11rdp, కానీ xorgxrdp రావడంతో, వాటి అవసరం అదృశ్యమైంది.
ఈ వ్యాసంలో మేము xorgxrdpని కవర్ చేయము.
xrdp ప్రాజెక్ట్, మునుపటి మాదిరిగానే, చాలా చిన్నది మరియు సుమారు 80 వేల లైన్లను కలిగి ఉంది.

మరిన్ని అక్షరదోషాలు
కోడ్ సారూప్య బ్లాక్ల సేకరణను కలిగి ఉంది. 87, 88, 89 లైన్లలో 'r', 'g', 'r' అంశాలను తనిఖీ చేయండి. 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++;
}
....
}
....
}ఈ కోడ్ librfxcodec లైబ్రరీ నుండి తీసుకోబడింది, ఇది RemoteFX కోసం jpeg2000 కోడెక్ను అమలు చేస్తుంది. ఇక్కడ, స్పష్టంగా, గ్రాఫిక్ డేటా ఛానెల్లు మిళితం చేయబడ్డాయి - “నీలం” రంగుకు బదులుగా, “ఎరుపు” రికార్డ్ చేయబడింది. కాపీ-పేస్ట్ ఫలితంగా ఈ లోపం ఎక్కువగా కనిపించింది.
ఇదే విధమైన ఫంక్షన్లో అదే సమస్య ఏర్పడింది rfx_encode_format_argb, ఇది ఎనలైజర్ కూడా మాకు చెప్పింది:
కోడ్ సారూప్య బ్లాక్ల సేకరణను కలిగి ఉంది. 260, 261, 262, 263 లైన్లలో 'a', 'r', 'g', 'r' అంశాలను తనిఖీ చేయండి. rfxencode_rgb_to_yuv.c 260
while (x < 64)
{
*la_buf++ = a;
*lr_buf++ = r;
*lg_buf++ = g;
*lb_buf++ = r;
x++;
}అర్రే డిక్లరేషన్
అర్రే ఓవర్రన్ సాధ్యమే. 'i — 8' సూచిక విలువ 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];
....
}
....
}ఈ రెండు ఫైల్లలోని శ్రేణి యొక్క డిక్లరేషన్ మరియు నిర్వచనం అననుకూలంగా ఉన్నాయి - పరిమాణం 1కి భిన్నంగా ఉంటుంది. అయినప్పటికీ, లోపాలు ఏవీ జరగవు - సరైన పరిమాణం evdev-map.c ఫైల్లో పేర్కొనబడింది, కాబట్టి హద్దులు దాటి ఏదీ లేదు. కాబట్టి ఇది సులభంగా పరిష్కరించబడే బగ్ మాత్రమే.
సరికాని పోలిక
షరతులతో కూడిన వ్యక్తీకరణలో ఒక భాగం ఎల్లప్పుడూ తప్పుగా ఉంటుంది: (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))
{
....
}
....
}ఫంక్షన్ టైప్ వేరియబుల్ని చదువుతుంది సంతకం చేయని చిన్నది వంటి వేరియబుల్ లోకి పూర్ణాంకానికి. ఇక్కడ తనిఖీ చేయడం అవసరం లేదు ఎందుకంటే మేము సంతకం చేయని వేరియబుల్ని చదువుతున్నాము మరియు ఫలితాన్ని పెద్ద వేరియబుల్కు కేటాయించాము, కాబట్టి వేరియబుల్ ప్రతికూల విలువను తీసుకోదు.
అనవసర తనిఖీలు
షరతులతో కూడిన వ్యక్తీకరణలో కొంత భాగం ఎల్లప్పుడూ నిజం: (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;
}
....
}మేము ఇప్పటికే ప్రారంభంలో పోలికను కలిగి ఉన్నందున అసమానత తనిఖీలు ఇక్కడ అర్ధవంతం కాదు. ఇది అక్షర దోషం మరియు డెవలపర్ ఆపరేటర్ను ఉపయోగించాలని కోరుకునే అవకాశం ఉంది || చెల్లని వాదనలను ఫిల్టర్ చేయడానికి.
తీర్మానం
ఆడిట్ సమయంలో, తీవ్రమైన లోపాలు ఏవీ గుర్తించబడలేదు, కానీ చాలా లోపాలు కనుగొనబడ్డాయి. అయినప్పటికీ, ఈ డిజైన్లు అనేక వ్యవస్థలలో ఉపయోగించబడతాయి, అయినప్పటికీ పరిధి తక్కువగా ఉంటుంది. ఒక చిన్న ప్రాజెక్ట్ తప్పనిసరిగా చాలా లోపాలను కలిగి ఉండదు, కాబట్టి మీరు చిన్న ప్రాజెక్ట్లలో మాత్రమే ఎనలైజర్ పనితీరును నిర్ధారించకూడదు. మీరు వ్యాసంలో దీని గురించి మరింత చదువుకోవచ్చు "".
మీరు మా నుండి PVS-Studio యొక్క ట్రయల్ వెర్షన్ని డౌన్లోడ్ చేసుకోవచ్చు .
మీరు ఈ కథనాన్ని ఇంగ్లీష్ మాట్లాడే ప్రేక్షకులతో పంచుకోవాలనుకుంటే, దయచేసి అనువాద లింక్ని ఉపయోగించండి: సెర్గీ లారిన్.
మూలం: www.habr.com
