PVS-Studio анализаторы арқылы rdesktop және xrdp тексеру

PVS-Studio анализаторы арқылы rdesktop және xrdp тексеру
Бұл RDP протоколымен жұмыс істеу үшін ашық бастапқы бағдарламаларды сынау туралы мақалалар сериясындағы екінші шолу. Онда біз rdesktop клиентін және xrdp серверін қарастырамыз.

Қателерді анықтау құралы ретінде пайдаланылады PVS-студиясы. Бұл Windows, Linux және macOS платформаларында қолжетімді C, C++, C# және Java тілдеріне арналған статикалық код анализаторы.

Мақалада маған қызықты болып көрінген қателер ғана берілген. Дегенмен, жобалар кішкентай, сондықтан қателіктер аз болды :).

ескерту. FreeRDP жобасын тексеру туралы алдыңғы мақаланы табуға болады осында.

жұмыс үстелі

жұмыс үстелі — UNIX негізіндегі жүйелер үшін RDP клиентін тегін енгізу. Cygwin астында жобаны құрастырсаңыз, оны Windows жүйесінде де пайдалануға болады. GPLv3 бойынша лицензияланған.

Бұл клиент өте танымал - ол әдепкі бойынша ReactOS жүйесінде пайдаланылады және сіз оған үшінші тарап графикалық интерфейстерін де таба аласыз. Дегенмен, ол әбден қартайған: оның алғашқы шығарылымы 4 жылы 2001 сәуірде болды - жазу кезінде ол 17 жаста.

Жоғарыда атап өткенімдей, жоба өте шағын. Ол шамамен 30 мың жол кодты қамтиды, бұл оның жасын ескере отырып, біртүрлі. Салыстыру үшін FreeRDP 320 мың жолды қамтиды. Міне, Cloc бағдарламасының нәтижесі:

PVS-Studio анализаторы арқылы rdesktop және xrdp тексеру

Қолжетімсіз код

V779 Қолжетімсіз код анықталды. Қате болуы мүмкін. 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);
}

Қате бізбен функцияда бірден кездеседі басты: оператордан кейін келетін кодты көреміз қайтару — бұл фрагмент жадты тазалауды орындайды. Дегенмен, қате қауіп төндірмейді: барлық бөлінген жадты бағдарлама шыққаннан кейін операциялық жүйе тазартады.

Қатені өңдеу жоқ

V557 Массивтің азаюы мүмкін. «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 пайдалану

V739 EOF мәнін «char» түрінің мәнімен салыстыруға болмайды. '(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 санына сәйкес келеді. Char. 0xFF символы ұнайды екен EOF (-1) файлдың соңы ретінде түсіндіріледі. Мұндай қателерді болдырмау үшін функцияның нәтижесі болып табылады fgetc сияқты айнымалыда сақталуы керек INT.

Қателер

1-фрагмент

V547 "Жазу_уақыты" өрнегі әрқашан жалған. 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-ге тең: бұл жағдайда біз тармақта боламыз тағы: айнымалы mod_time келесі шартқа қарамастан әрқашан 0 болады.
  • Айнымалылардың бірі 0: mod_time 0-ге тең болады (басқа айнымалының теріс емес мәні болған жағдайда), өйткені MIN екі опцияның кішісін таңдайды.
  • Екі айнымалы да 0-ге тең емес: ең аз мәнді таңдаңыз.

Шартты ауыстырғанда жазу_уақыты && уақыт_өзгерту мінез-құлық дұрыс көрінеді:

  • Айнымалылардың бірі немесе екеуі де 0-ге тең емес: нөлдік емес мәнді таңдаңыз.
  • Екі айнымалы да 0-ге тең емес: ең аз мәнді таңдаңыз.

2-фрагмент

V547 Өрнек әрқашан шындық. Мұнда «&&» операторын пайдалану керек шығар. 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 мәні болуы мүмкін емес.

Шексіз жолды көшіру

V512 '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, ….).

Артық жағдай

V560 Шартты өрнектің бөлігі әрқашан ақиқат: add > 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

xrdp — ашық бастапқы кодпен RDP серверін жүзеге асыру. Жоба 2 бөлікке бөлінген:

  • xrdp – хаттаманы жүзеге асыру. Apache 2.0 лицензиясы бойынша таратылады.
  • xorgxrdp - xrdp-мен пайдалануға арналған Xorg драйверлерінің жинағы. Лицензия - X11 (MIT сияқты, бірақ жарнамада пайдалануға тыйым салады)

Жобаны әзірлеу rdesktop және FreeRDP нәтижелеріне негізделген. Бастапқыда графикамен жұмыс істеу үшін жеке VNC серверін немесе RDP қолдауы бар арнайы X11 серверін - X11rdp пайдалану керек болды, бірақ xorgxrdp пайда болған кезде оларға қажеттілік жойылды.

Бұл мақалада біз xorgxrdp-ті қарастырмаймыз.

xrdp жобасы, алдыңғы сияқты, өте кішкентай және шамамен 80 мың жолды қамтиды.

PVS-Studio анализаторы арқылы rdesktop және xrdp тексеру

Қосымша қателер

V525 Код ұқсас блоктар жинағын қамтиды. 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++;
      }
      ....
  }
  ....
}

Бұл код RemoteFX үшін jpeg2000 кодегін жүзеге асыратын librfxcodec кітапханасынан алынды. Мұнда, шамасы, графикалық деректер арналары араласады - «көк» түстің орнына «қызыл» жазылады. Бұл қате көшіру-қою нәтижесінде пайда болуы мүмкін.

Ұқсас функцияда бірдей мәселе орын алды rfx_encode_format_argb, оны анализатор бізге де айтты:

V525 Код ұқсас блоктар жинағын қамтиды. 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++;
}

Массив декларациясы

V557 Массивтің асып кетуі мүмкін. '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 файлында көрсетілген, сондықтан шекарадан тыс шектеулер жоқ. Сондықтан бұл оңай түзетілетін қате ғана.

Дұрыс емес салыстыру

V560 Шартты өрнек бөлігі әрқашан жалған болады: (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))
  {
    ....
  }
  ....
}

Функция түрдегі айнымалыны оқиды қолсыз қысқа сияқты айнымалыға айналады INT. Мұнда тексеру қажет емес, себебі біз таңбасыз айнымалыны оқып, нәтижені үлкенірек айнымалыға тағайындаймыз, сондықтан айнымалы теріс мән қабылдай алмайды.

Қажетсіз тексерулер

V560 Шартты өрнектің бөлігі әрқашан ақиқат болады: (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 сынақ нұсқасын жүктей аласыз сайт.

PVS-Studio анализаторы арқылы rdesktop және xrdp тексеру

Егер сіз осы мақаланы ағылшын тілді аудиториямен бөліскіңіз келсе, аударма сілтемесін пайдаланыңыз: Сергей Ларин. PVS-Studio көмегімен rdesktop және xrdp тексеру

Ақпарат көзі: www.habr.com

пікір қалдыру