首頁  >  文章  >  後端開發  >  TIL CANnex K 存在,但您不應該使用它

TIL CANnex K 存在,但您不應該使用它

DDD
DDD原創
2024-11-03 13:58:03338瀏覽

TIL CAnnex K exists but you shouldn

Annex K 是技術名稱。其他常見關鍵字是 __STDC_LIB_EXT1__ 和 __STDC_WANT_LIB_EXT1__。附件 K 定義了「安全性」_s 後綴,例如 sprintf_s() 和 scanf_s()。

也請參閱附錄 K (2015) 的現場經驗和邊界檢查 - cppreference.com 技術文件。

目標

_s() 函數有什麼意義?他們檢查參數是否有更多不變量,例如「如果流為空、字串為空、bufsz 為零或緩衝區寫入超出指定長度的越界,則將呼叫約束處理程序」。這似乎是個好主意,對吧?是的!確實如此!

重點是你可以/可以這樣做:

#define __STDC_WANT_LIB_EXT1__ 1
#include ade979de5fc0e1ca0540f360a64c230b

int main() {
  printf_s("Hello %s!\n", "Alan Turing");
  return 0;
}

這與沒有 __STDC_WANT_LIB_EXT1__ 的正常做事方式相比如何?

幸福之路

FILE *file = fopen("hello.txt", "r");
// file is OK.
FILE *file;
errno_t err = fopen_s(&file, "hello.txt", "r");
// file is OK

悲傷之路

FILE *file = fopen("notexist.txt", "r");
// file is NULL, errno is set.
FILE *file;
errno_t err = fopen_s(&file, "notexist.txt", "r");
// file is NULL, err is set.

壞路

FILE *file = fopen(NULL, NULL);
// idk.
FILE *file;
errno_t err = fopen_s(&file, NULL, NULL);
// Constraint violated. Abort with message.

是的,您可以自訂約束處理程序以僅登入檔案並繼續,就像什麼都沒發生一樣。

set_constraint_handler_s(ignore_handler_s);
set_constraint_handler_s(abort_handler_s);
set_constraint_handler_s(my_awesome_handler);

注意到普通的 fopen() 如何具有相同的回傳值(可能不同的 errno)來指示不同層級的錯誤嚴重程度?這就是 fopen_s() 試圖改進的地方。至少,這是我讀到的。我認為它就像 Rust 的panic!() 與返回的 Result 相比。它也可能透過提供 size_of_dest 參數來幫助阻止一些緩衝區溢位攻擊,以避免溢位任何目標緩衝區,如 strcpy_s() 和 gets_s()。

char* gets( char* str ); // (removed in C11)
char* gets_s( char* str, rsize_t n ); // (since C11, annex K)

將 stdin 讀入 str 指向的字元陣列中,直到找到換行符號或發生檔案結尾。在讀入數組的最後一個字元之後立即寫入一個空字元。換行符被丟棄,但不儲存在緩衝區中。

gets() 函數不執行邊界檢查,因此此函數極易受到緩衝區溢位攻擊。它不能安全使用(除非程式運行在限制 stdin 上顯示內容的環境中)。因此,函數在 C99 標準的第三次勘誤中已被棄用,並在 C11 標準中完全刪除。 fgets() 和 gets_s() 是推薦的替代品。

警告:切勿使用 gets()。

// BAD
char buffer[1000];
gets(buffer);
// ⚠️ Could write >1000 chars to `buffer`!
// GOOD
char buffer[1000];
gets_s(buffer, sizeof(buffer));
// This will stop at 1000 chars.

_s() 函數似乎非常適合阻止可能發生緩衝區溢位的常見位置。

問題

它們並沒有在所有地方實現。 _s() 函數是一個擴充,在 GNU 的 glibc 等 libc 實作中不可用。還有其他一些小問題,例如它對於多執行緒來說不符合人體工學,以及對於strcpy_s() 之類的東西使用sizeof(src) 而不是sizeof(dest) 的常見錯誤,但與可用性問題相比,所有這些都顯得蒼白無力。

我能找到的大多數線上資訊似乎表明 MSVC 是唯一實作了附件 K 的主要編譯器/libc。

鑑於這些花哨的 _s() 函數並不存在於您的程式碼需要編譯的所有地方,您需要編寫以下程式碼:

#define __STDC_WANT_LIB_EXT1__ 1
#include ade979de5fc0e1ca0540f360a64c230b

int main() {
  printf_s("Hello %s!\n", "Alan Turing");
  return 0;
}

...對於您想要執行 strlen_s() 或 fopen_s() 或 strcpy_s() 的每個實例。這是發瘋的好方法。

很明顯,你不會寫依賴平台的程式碼只是為了執行基本的 printf() 和 strcpy() 但是將所有 #ifdef __STDC_LIB_EXT1__ #else 內容包裝在庫中怎麼樣?

我透過 Google 快速搜尋發現了兩個看起來很有前途的函式庫:

  • safec:安全 C 庫網站 GitHub 頁面 ⭐335
  • sbaresearch/slibc:C11 附件 K「邊界檢查介面」ISO/IEC 9899:2011 ⭐14
  • 的實現

所以...如果您想(或安全人員要求)使用 _s() 函數,但又不想將自己限制為 MSVC,那麼您可以使用其中一個 ☝ 函式庫。

?如需了解更多信息,請參閱附錄 K (2015) 的現場經驗和邊界檢查 - cppreference.com 技術文件。

以上是TIL CANnex K 存在,但您不應該使用它的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn