使用 PVS-Studio 分析器檢查 rdesktop 和 xrdp

使用 PVS-Studio 分析器檢查 rdesktop 和 xrdp
這是有關測試使用 RDP 協定的開源程式的系列文章中的第二篇評論。 在其中我們將了解 rdesktop 客戶端和 xrdp 伺服器。

用作識別錯誤的工具 PVS工作室。 它是 C、C++、C# 和 Java 語言的靜態程式碼分析器,可在 Windows、Linux 和 macOS 平台上使用。

這篇文章只介紹了那些我覺得有趣的錯誤。 不過,項目很小,所以錯誤很少:)。

注意。 之前關於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,然後數組將溢出 產量.

在 char 類型中使用 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++;
  }
  ....
}

在這裡我們看到到達文件末尾的錯誤處理:如果 弗吉特 傳回一個代碼為 0xFF 的字符,它將被解釋為檔案結尾(EOF).

EOF 它是一個常數,通常定義為-1。 例如,在 CP1251 編碼中,俄語字母表的最後一個字母的代碼為 0xFF,如果我們談論像這樣的變量,則對應於數字 -1 。 結果發現符號0xFF,就像 EOF (-1) 被解釋為文件結尾。 為了避免此類錯誤,該函數的結果是 弗吉特 應該儲存在像這樣的變數中 INT.

打字錯誤

片段1

V547 表達式“write_time”始終為 false。 磁碟.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

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);
  ....
}

當您完整地查看該函數時,您會發現這段程式碼不會導致問題。 然而,它們將來可能會出現:一個不小心的更改,我們就會遇到緩衝區溢位 - 的sprintf 不受任何限制,因此在連接路徑時我們可以超越數組的邊界。 建議注意此電話 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++;
      }
      ....
  }
  ....
}

此程式碼取自 librfxcodec 函式庫,該函式庫為 RemoteFX 實作了 jpeg2000 編解碼器。 顯然,這裡的圖形資料通道是混合的 - 記錄的不是“藍色”,而是“紅色”。 此錯誤很可能是由於複製貼上而出現的。

類似的函數也出現同樣的問題 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

如果您想與英語讀者分享這篇文章,請使用翻譯連結:Sergey Larin。 使用 PVS-Studio 檢查 rdesktop 和 xrdp

來源: www.habr.com

添加評論