這是有關測試使用 RDP 協定的開源程式的系列文章中的第二篇評論。 在其中我們將了解 rdesktop 客戶端和 xrdp 伺服器。
用作識別錯誤的工具
這篇文章只介紹了那些我覺得有趣的錯誤。 不過,項目很小,所以錯誤很少:)。
注意。 之前關於FreeRDP計畫驗證的文章可以找到
桌面
這個客戶端非常受歡迎——ReactOS 中預設使用它,您還可以找到它的第三方圖形前端。 然而,他已經相當老了:他的第一次發布發生在 4 年 2001 月 17 日——在撰寫本文時,他已經 XNUMX 歲了。
正如我之前指出的,該項目非常小。 它包含大約 30 萬行程式碼,考慮到它的歷史,這有點奇怪。 作為比較,FreeRDP 包含 320 萬行。 以下是 Cloc 程式的輸出:
無法存取的程式碼
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,然後數組將溢出 產量.
在 char 類型中使用 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++;
}
....
}
在這裡我們看到到達文件末尾的錯誤處理:如果 弗吉特 傳回一個代碼為 0xFF 的字符,它將被解釋為檔案結尾(EOF).
EOF 它是一個常數,通常定義為-1。 例如,在 CP1251 編碼中,俄語字母表的最後一個字母的代碼為 0xFF,如果我們談論像這樣的變量,則對應於數字 -1 輅。 結果發現符號0xFF,就像 EOF (-1) 被解釋為文件結尾。 為了避免此類錯誤,該函數的結果是 弗吉特 應該儲存在像這樣的變數中 INT.
打字錯誤
片段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);
....
}
當您完整地查看該函數時,您會發現這段程式碼不會導致問題。 然而,它們將來可能會出現:一個不小心的更改,我們就會遇到緩衝區溢位 - 的sprintf 不受任何限制,因此在連接路徑時我們可以超越數組的邊界。 建議注意此電話 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 的特殊 X11 伺服器 - X11rdp,但隨著 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。 這裡不需要檢查,因為我們正在讀取一個無符號變數並將結果分配給一個更大的變量,因此該變數不能取負值。
不必要的檢查
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