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-нд болсон - бичиж байх үед тэр XNUMX настай.

Миний өмнө дурдсанчлан, төсөл нь маш бага юм. Энэ нь ойролцоогоор 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 тоотой тохирч байна. Хорхой. Энэ нь 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 Илэрхийлэл нь үргэлж үнэн байдаг. Энд '&&' операторыг ашиглах нь зүйтэй болов уу. диск.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' функцийг дуудах нь буфер 'fullpath' халихад хүргэнэ. диск.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 Нөхцөлт илэрхийллийн хэсэг нь үргэлж үнэн байдаг: нэмэх > 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 дэмжлэгтэй X11rdp тусгай X11 сервер ашиглах шаардлагатай байсан боловч 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

сэтгэгдэл нэмэх