C++標準庫中有哪些危險函數?

如題,並指出解決方法。
比如對於strcpy,如果目的地址所指的內存空間不夠大,可能會造成緩衝溢出的錯誤情況,因此使用strncpy會更安全。


上面主要說了 C 庫函數的問題。 C 和 C++ 之間的問題有 realloc 和 longjmp 。

realloc 封裝的步驟有點多。如果想要原位擴充的福利,就不得不面對原位擴充失敗後強制逐位移動的問題。這使得 realloc 使用較為受限(只能用於可平凡複製或可逐位移動的類型)。

longjmp 也能實現異常控制流,然而它會跳過析構函數,若被跳過的析構函數有非平凡者則導致 UB 。尤其是用析構函數釋放資源時不能用 longjmp 。

realloc 在使用 C++ 設施時一般用不上,有時 vector 可滿足類似要求。不過如果你想把它封裝到模板里,可以用 is_trivially_copyable 拒絕掉不可平凡複製的 T 。

在無法確定 longjmp 能用的場合,用異常實現異常控制流即可。


大致講幾個我知道的。不是研究二進位的,才疏學淺,如有遺漏或錯誤請打臉。

  • 現(C++14、C11)已廢除的gets,在輸入出現換行前會一直讀入,絕對不要用,建議使用fgets或gets_s(C11),這兩個限制了讀入的長度。
  • scanf系列,漏洞在於其本身對%s的處理,scanf("%s", buf)容易出現和gets一樣原因導致緩衝區溢出,可以通過%9s等方式限定長度,或者使用scanf_s(C99)替代,%s需要提供指針和長度兩個參數來限制,靈活性自然是後者更高。
  • sprintf系列,容易因為輸出信息過長而導致緩衝區溢出,使用snprintf(C99),其第二個參數限制了輸出到buffer的位元組數,(仍然存在危險性的替代品)sprintf的格式化字元串里限制Field width限制沒部分的長度sprintf_s(C11)限制了%s的長度。或者使用snprintf_s(C11)。
  • 一堆的str***系列,這個大概不需要解釋,方法是用對應的mem***系列,或者有些有str***_s可以補救。
  • ato*系列,atof atoi atol atoll(C99)等,若字元串表示的數字溢出就是ub。C中用strto*系列替代,C++17多出了to_chars和from_chars函數,如果字元串表示的數字溢出則(設置errno或)返回錯誤。

下面這些函數不是標準定義本身危險,而是使用的方式不當會導致漏洞。實際上應該都是ub了。

  • printf系列。格式化字元串漏洞,如果不可信輸入直接作為format string即第一個參數傳入,可以做到任意內存讀(寫,這個需要支持POSIX)。

對指針的處理不當導致的漏洞

  • free函數,對野指針的使用可能會導致UAF漏洞。
  • realloc函數,它的功能太多,十分容易導致野指針出現。

大概已經跑題了,就這些吧。

—————— 大概是分割線 ——————

你說這破乎怎麼就不支持內聯代碼塊呢?


這個問題一言以蔽之,對比參照微軟出的那套安全函數庫(_s)就可以知道標準庫中可能會出問題的函數有哪些了


函數庫裡面有_s,那麼原來的函數一定有問題


The only correct implementation of gets() after 1989 is
abort();


看了一圈,基本都是C挖的坑…


strncpy同樣是危險分子,它雖然自己不會溢出緩衝區,但超長時不加"",仍然是很大的安全隱患。

解決方案:用strcpy_s,沒有的環境自己封裝一個;或者用snprintf;或者多用C++的std::string,C的字元串操作能避免就避免。


整體來說,建議按照內存操作,如memcpy等,字元串操作如strcpy等分一下比較好。

當然直接把VS裡邊實現的_s找出來也是一個方式。

另外還可以直接去下載CISCO的開源安全函數庫,看看源碼也挺好


最簡單的我就覺得三角函數有坑,幅度角度,有的人說沒問題,也的確沒問題。


strlen 也是不安全的。它假設參數是個合法的(以NUL結尾的)字元串。然而對於外來的字元串,不能確定它一定合法,並且不存在判斷一個字元串是否合法的辦法。所以請用 std::string。


這個看對外還是對內

比如某個地方需要列印正在Debug的信息,我認為printf("%p",pointer),我認為無傷大雅,以為明確的知道這個對象可能是什麼,是否是可控,造成奔潰之後是否有問題

但是如果是不可控的,依賴外部輸入的,這些函數需要慎之又慎,內存溢出/範圍非法內存等,都時時刻刻考驗編寫者的危機意識


char* strcpy(char* dest, const char* src);
char* gets(char* buf);
int sprintf(char* restrict buf, const char* restrict format, ...);


調用者在使用上述函數時不能指定緩衝區得長度,這樣就可能造成緩衝區溢出(如若該行長於緩衝區長度),寫到緩衝區之後的存儲空間中,從而產生不可預料的後果。


strcpy應使用strncpy進行替換
gets應使用fgets進行替換
sprintf應使用snprintf進行替換


推薦閱讀:

如何學好編程語言以及進行軟體開發?
美劇《矽谷》第三季第一集神秘代碼寫的是什麼?
C++ 有哪些鮮為人知的奇特操作?
以C++為核心語言的高頻交易系統是如何做到低延遲的?

TAG:測試 | 代碼質量 | 代碼分析 | 標準庫 | CC |