當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > strongerHuang
[導(dǎo)讀]C中大多數(shù)緩沖區(qū)溢出問(wèn)題可以直接追溯到標(biāo)準(zhǔn) C 庫(kù)。最有害的罪魁禍?zhǔn)资遣贿M(jìn)行自變量檢查的、有問(wèn)題的字符串操作strcpy、strcat、sprintf?和?gets。大部分程序員仍然會(huì)使用這些函數(shù),因?yàn)閺膩?lái)沒(méi)有人教開(kāi)發(fā)人員避免使用它們。某些人從各處獲得某個(gè)提示,但即使是優(yōu)秀的開(kāi)發(fā)人員也會(huì)被這弄糟,下面就來(lái)分析一下。

來(lái)源:嵌入式ARM

C中大多數(shù)緩沖區(qū)溢出問(wèn)題可以直接追溯到標(biāo)準(zhǔn) C 庫(kù)。最有害的罪魁禍?zhǔn)资遣贿M(jìn)行自變量檢查的、有問(wèn)題的字符串操作strcpy、strcat、sprintf和?gets。

大部分程序員仍然會(huì)使用這些函數(shù),因?yàn)閺膩?lái)沒(méi)有人教開(kāi)發(fā)人員避免使用它們。某些人從各處獲得某個(gè)提示,但即使是優(yōu)秀的開(kāi)發(fā)人員也會(huì)被這弄糟,下面就來(lái)分析一下。

第一位公共的“敵人是?gets()。建議不要使用?gets()。該函數(shù)從標(biāo)準(zhǔn)輸入讀入用戶輸入的一行文本,它在遇到?EOF字符或換行字符之前,不會(huì)停止讀入文本。也就是:gets()根本不執(zhí)行邊界檢查。因此,使用?gets()總是有可能使任何緩沖區(qū)溢出。作為一個(gè)替代方法,可以使用?fgets()。它可以做與?gets()所做的同樣的事情,但它接受用來(lái)限制讀入字符數(shù)目的大小參數(shù),因此,提供了一種防止緩沖區(qū)溢出的方法。例如,不要使用以下代碼:

void main()
{
    char buf[1024];
    gets(buf);
}

而使用以下代碼:

#define BUFSIZE 1024
void main()
{
    char buf[BUFSIZE];
    fgets(buf, BUFSIZE, stdin);
}

C 編程中的主要陷阱

C語(yǔ)言中一些標(biāo)準(zhǔn)函數(shù)很有可能使你陷入困境。但不是所有函數(shù)使用都不好。通常,利用這些函數(shù)之一需要任意輸入傳遞給該函數(shù)。這個(gè)列表包括:

  • strcpy()
  • strcat()
  • sprintf()
  • scanf()
  • sscanf()
  • fscanf()
  • vfscanf()
  • vsprintf
  • vscanf()
  • vsscanf()
  • streadd()
  • strecpy()
  • strtrns()

我們推薦使用,如果有可能,應(yīng)盡量避免使用這些函數(shù)。這些函數(shù)在大多數(shù)情況下,都有合理的替代方法。我們將仔細(xì)檢查它們中的每一個(gè),所以可以看到什么構(gòu)成了它們的誤用,以及如何避免它。

strcpy()函數(shù)將源字符串復(fù)制到緩沖區(qū)。沒(méi)有指定要復(fù)制字符的具體數(shù)目。復(fù)制字符的數(shù)目直接取決于源字符串中的數(shù)目。如果源字符串碰巧來(lái)自用戶輸入,且沒(méi)有專(zhuān)門(mén)限制其大小,則有可能會(huì)陷入大的麻煩中!

如果知道目的地緩沖區(qū)的大小,則可以添加明確的檢查:

if(strlen(src) >= dst_size)
{
  /* Do something appropriate, such as throw an error. */
}
else
{
  strcpy(dst, src);
}

完成同樣目的的更容易方式是使用 strncpy() 庫(kù)例程:

strncpy(dst, src, dst_size-1);
dst[dst_size-1] = '\0'; /* Always do this to be safe! */

如果 src 比 dst 大,則該函數(shù)不會(huì)拋出一個(gè)錯(cuò)誤;當(dāng)達(dá)到最大尺寸時(shí),它只是停止復(fù)制字符。注意上面調(diào)用?strncpy()中的 -1。如果 src 比 dst 長(zhǎng),則那給我們留有空間,將一個(gè)空字符放在 dst 數(shù)組的末尾。

當(dāng)然,可能使用strcpy()不會(huì)帶來(lái)任何潛在的安全性問(wèn)題,正如在以下示例中所見(jiàn):

strcpy(buf, "Hello!");

即使這個(gè)操作造成 buf 的溢出,但它只是對(duì)幾個(gè)字符這樣而已。由于我們靜態(tài)地知道那些字符是什么,并且很明顯,由于沒(méi)有危害,所以這里無(wú)須擔(dān)心 ― 當(dāng)然,除非可以用其它方式覆蓋字符串Hello所在的靜態(tài)存儲(chǔ)器。

確保strcpy()不會(huì)溢出的另一種方式是,在需要它時(shí)就分配空間,確保通過(guò)在源字符串上調(diào)用 strlen() 來(lái)分配足夠的空間。例如:

dst = (char *)malloc(strlen(src));
strcpy(dst, src);

strcat()函數(shù)非常類(lèi)似于strcpy(),除了它可以將一個(gè)字符串合并到緩沖區(qū)末尾。它也有一個(gè)類(lèi)似的、更安全的替代方法strncat()。如果可能,使用strncat()而不要使用strcat()。

函數(shù)sprintf()和vsprintf()是用來(lái)格式化文本和將其存入緩沖區(qū)的通用函數(shù)。它們可以用直接的方式模仿strcpy()行為。換句話說(shuō),使用sprintf()和vsprintf()與使用strcpy()一樣,都很容易對(duì)程序造成緩沖區(qū)溢出。例如,考慮以下代碼:

void main(int argc, char **argv)
{
    char usage[1024];
    sprintf(usage, "USAGE: %s -f flag [arg1]\n", argv[0]);
}

我們經(jīng)常會(huì)看到類(lèi)似上面的代碼。它看起來(lái)沒(méi)有什么危害。它創(chuàng)建一個(gè)知道如何調(diào)用該程序字符串。那樣,可以更改二進(jìn)制的名稱(chēng),該程序的輸出將自動(dòng)反映這個(gè)更改。雖然如此, 該代碼有嚴(yán)重的問(wèn)題。文件系統(tǒng)傾向于將任何文件的名稱(chēng)限制于特定數(shù)目的字符。那么,您應(yīng)該認(rèn)為如果您的緩沖區(qū)足夠大,可以處理可能的最長(zhǎng)名稱(chēng),您的程序會(huì)安全,對(duì)嗎?只要將1024改為對(duì)我們的操作系統(tǒng)適合的任何數(shù)目,就好了嗎?但是,不是這樣的。通過(guò)編寫(xiě)我們自己的小程序來(lái)推翻上面所說(shuō)的,可能容易地推翻這個(gè)限制:

void main()
{
  execl("/path/to/above/program",
        <>,
        NULL);
}

函數(shù)execl()啟動(dòng)第一個(gè)參數(shù)中命名的程序。第二個(gè)參數(shù)作為argv[0]傳遞給被調(diào)用的程序。我們可以使那個(gè)字符串要多長(zhǎng)有多長(zhǎng)!

那么如何解決sprintf()帶來(lái)得問(wèn)題呢?遺憾的是,沒(méi)有完全可移植的方法。某些體系結(jié)構(gòu)提供了snprintf()方法,即允許程序員指定將多少字符從每個(gè)源復(fù)制到緩沖區(qū)中。例如,如果我們的系統(tǒng)上有snprintf,則可以修正一個(gè)示例成為:

void main(int argc, char **argv)
{
    char usage[1024];
    char format_string = "USAGE: %s -f flag [arg1]\n";
    snprintf(usage, format_string, argv[0],1024-strlen(format_string) + 1);
}

注意,在第四個(gè)變量之前,snprintf()與sprintf()是一樣的。第四個(gè)變量指定了從第三個(gè)變量中應(yīng)被復(fù)制到緩沖區(qū)的字符最大數(shù)目。注意,1024 是錯(cuò)誤的數(shù)目!我們必須確保要復(fù)制到緩沖區(qū)使用的字符串總長(zhǎng)不超過(guò)緩沖區(qū)的大小。所以,必須考慮一個(gè)空字符,加上所有格式字符串中的這些字符,再減去格式說(shuō)明符 %s。該數(shù)字結(jié)果為1000,但上面的代碼是更具有可維護(hù)性,因?yàn)槿绻袷阶址既话l(fā)生變化,它不會(huì)出錯(cuò)。

sprintf()的許多(但不是全部)版本帶有使用這兩個(gè)函數(shù)的更安全的方法。可以指定格式字符串本身每個(gè)自變量的精度。例如,另一種修正上面有問(wèn)題的sprintf()的方法是:

void main(int argc, char **argv)
{
    char usage[1024];
    sprintf(usage, "USAGE: %.1000s -f flag [arg1]\n", argv[0]);
}

注意,百分號(hào)后與 s 前的 .1000。該語(yǔ)法表明,從相關(guān)變量(本例中是?argv[0])復(fù)制的字符不超過(guò) 1000 個(gè)。

如果任一解決方案在您的程序必須運(yùn)行的系統(tǒng)上行不通,則最佳的解決方案是將snprintf()的工作版本與您的代碼放置在一個(gè)包中??梢哉业揭?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-family: " operator="" mono",="" consolas,="" monaco,="" menlo,="" monospace;font-size:="" 14px;border-radius:="" 4px;color:="" rgb(239,="" 112,="" 96);background-color:="" rgba(27,="" 31,="" 35,="" 0.047);word-break:="" break-all;"="">sh歸檔格式的、自由使用的版本;請(qǐng)參閱 參考資料。

繼續(xù),scanf系列的函數(shù)也設(shè)計(jì)得很差。在這種情況下,目的地緩沖區(qū)會(huì)發(fā)生溢出??紤]以下代碼:

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%s", &buf);
}

如果輸入的字大于 buf 的大小,則有溢出的情況。幸運(yùn)的是,有一種簡(jiǎn)便的方法可以解決這個(gè)問(wèn)題。考慮以下代碼,它沒(méi)有安全性方面的薄弱環(huán)節(jié):

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

百分號(hào)和 s 之間的 255 指定了實(shí)際存儲(chǔ)在變量 buf 中來(lái)自 argv[0] 的字符不會(huì)超過(guò) 255 個(gè)。其余匹配的字符將不會(huì)被復(fù)制。

接下來(lái),我們討論streadd()和strecpy()。由于,不是每臺(tái)機(jī)器開(kāi)始就有這些調(diào)用,那些有這些函數(shù)的程序員,在使用它們時(shí),應(yīng)該小心。這些函數(shù)可以將那些含有不可讀字符的字符串轉(zhuǎn)換成可打印的表示。例如,考慮以下程序:

#include 

void main(int argc, char **argv)
{
    char buf[20];
    streadd(buf, "\t\n", "");
    printf(%s\n", buf);
}

該程序打?。?

\t\n

而不是打印所有空白。如果程序員沒(méi)有預(yù)料到需要多大的輸出緩沖區(qū)來(lái)處理輸入緩沖區(qū)(不發(fā)生緩沖區(qū)溢出),則streadd()和?strecpy()函數(shù)可能有問(wèn)題。如果輸入緩沖區(qū)包含單一字符 ― 假設(shè)是?ASCII 001(control-A)―則它將打印成四個(gè)字符\001。這是字符串增長(zhǎng)的最壞情況。如果沒(méi)有分配足夠的空間,以至于輸出緩沖區(qū)的大小總是輸入緩沖區(qū)大小的四倍,則可能發(fā)生緩沖區(qū)溢出。

另一個(gè)較少使用的函數(shù)是strtrns(),因?yàn)樵S多機(jī)器上沒(méi)有該函數(shù)。函數(shù)strtrns()取三個(gè)字符串和結(jié)果字符串應(yīng)該放在其內(nèi)的一個(gè)緩沖區(qū),作為其自變量。第一個(gè)字符串必須復(fù)制到該緩沖區(qū)。一個(gè)字符被從第一個(gè)字符串中復(fù)制到緩沖區(qū),除非那個(gè)字符出現(xiàn)在第二個(gè)字符串中。如果出現(xiàn)的話,那么會(huì)替換掉第三個(gè)字符串中同一索引中的字符。這聽(tīng)上去有點(diǎn)令人迷惑。讓我們看一下,將所有小寫(xiě)字符轉(zhuǎn)換成大寫(xiě)字符的示例:

#include 
void main(int argc, char **argv)
{
    char lower[] = "abcdefghijklmnopqrstuvwxyz";
    char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    char *buf;
    if(argc < 2) { printf("USAGE: %s arg\n", argv[0]); exit(0); } buf = (char *)malloc(strlen(argv[1])); strtrns(argv[1], lower, upper, buf); printf("%s\n", buf); }

以上代碼實(shí)際上不包含緩沖區(qū)溢出。但如果我們使用了固定大小的靜態(tài)緩沖區(qū),而不是用malloc()分配足夠空間來(lái)復(fù)制argv[1],則可能會(huì)引起緩沖區(qū)溢出情況。

避免內(nèi)部緩沖區(qū)溢出

realpath()函數(shù)接受可能包含相對(duì)路徑的字符串,并將它轉(zhuǎn)換成指同一文件的字符串,但是通過(guò)絕對(duì)路徑。在做這件事時(shí),它展開(kāi)了所有符號(hào)鏈接。

該函數(shù)取兩個(gè)自變量,第一個(gè)作為要規(guī)范化的字符串,第二個(gè)作為將存儲(chǔ)結(jié)果的緩沖區(qū)。當(dāng)然,需要確保結(jié)果緩沖區(qū)足夠大,以處理任何大小的路徑。分配的MAXPATHLEN緩沖區(qū)應(yīng)該足夠大。然而,使用realpath()有另一個(gè)問(wèn)題。如果傳遞給它的、要規(guī)范化的路徑大小大于MAXPATHLEN,則realpath()實(shí)現(xiàn)內(nèi)部的靜態(tài)緩沖區(qū)會(huì)溢出!雖然實(shí)際上沒(méi)有訪問(wèn)溢出的緩沖區(qū),但無(wú)論如何它會(huì)傷害您的。結(jié)果是,應(yīng)該明確不使用realpath(),除非確保檢查您試圖規(guī)范化的路徑長(zhǎng)度不超過(guò)MAXPATHLEN。

其它廣泛可用的調(diào)用也有類(lèi)似的問(wèn)題。經(jīng)常使用的syslog()調(diào)用也有類(lèi)似的問(wèn)題,直到不久前,才注意到這個(gè)問(wèn)題并修正了它。大多數(shù)機(jī)器上已經(jīng)糾正了這個(gè)問(wèn)題,但您不應(yīng)該依賴正確的行為。最好總是假定代碼正運(yùn)行在可能最不友好的環(huán)境中,只是萬(wàn)一在哪天它真的這樣。getopt()系列調(diào)用的各種實(shí)現(xiàn),以及getpass()函數(shù),都可能產(chǎn)生內(nèi)部靜態(tài)緩沖區(qū)溢出問(wèn)題。如果您不得不使用這些函數(shù),最佳解決方案是設(shè)置傳遞給這些函數(shù)的輸入長(zhǎng)度的閾值。

自己模擬gets()的安全性問(wèn)題以及所有問(wèn)題是非常容易的。例如,下面這段代碼:

char buf[1024];
int i = 0;
char ch;
while((ch = getchar()) != '\n')
{
    if(ch == -1) break;
    buf[i++] = ch;
}

哎呀!可以用來(lái)讀入字符的任何函數(shù)都存在這個(gè)問(wèn)題,包括getchar()、fgetc()、getc()和?read()。

緩沖區(qū)溢出問(wèn)題的準(zhǔn)則是:總是確保做邊界檢查。

C 和 C++ 不能夠自動(dòng)地做邊界檢查,這實(shí)在不好,但確實(shí)有很好的原因,來(lái)解釋不這樣做的理由。邊界檢查的代價(jià)是效率。一般來(lái)講,C 在大多數(shù)情況下注重效率。然而,獲得效率的代價(jià)是,C 程序員必須十分警覺(jué),并且有極強(qiáng)的安全意識(shí),才能防止他們的程序出現(xiàn)問(wèn)題,而且即使這些,使代碼不出問(wèn)題也不容易。

在現(xiàn)在,變量檢查不會(huì)嚴(yán)重影響程序的效率。大多數(shù)應(yīng)用程序不會(huì)注意到這點(diǎn)差異。所以,應(yīng)該總是進(jìn)行邊界檢查。在將數(shù)據(jù)復(fù)制到您自己的緩沖區(qū)之前,檢查數(shù)據(jù)長(zhǎng)度。同樣,檢查以確保不要將過(guò)大的數(shù)據(jù)傳遞給另一個(gè)庫(kù),因?yàn)槟膊荒芟嘈牌渌说拇a?。ɑ貞浺幌虑懊嫠懻摰膬?nèi)部緩沖區(qū)溢出。)

其它危險(xiǎn)是什么?

遺憾的是,即使是系統(tǒng)調(diào)用的“安全”版本 ― 譬如,相對(duì)于strcpy()的strncpy()也不完全安全。也有可能把事情搞糟。即使安全的調(diào)用有時(shí)會(huì)留下未終止的字符串,或者會(huì)發(fā)生微妙的相差一位錯(cuò)誤。當(dāng)然,如果您偶然使用比源緩沖區(qū)小的結(jié)果緩沖區(qū),則您可能發(fā)現(xiàn)自己處于非常困難的境地。

與我們目前所討論的相比,往往很難犯這些錯(cuò)誤,但您應(yīng)該仍然意識(shí)到它們。當(dāng)使用這類(lèi)調(diào)用時(shí),要仔細(xì)考慮。如果不仔細(xì)留意緩沖區(qū)大小,包括bcopy()、fgets()、memcpy()、snprintf()、strccpy()、strcadd()、strncpy()和?vsnprintf(),許多函數(shù)會(huì)行為失常。

另一個(gè)要避免的系統(tǒng)調(diào)用是?getenv()。使用getenv()的最大問(wèn)題是您從來(lái)不能假定特殊環(huán)境變量是任何特定長(zhǎng)度的。我們將在后續(xù)的專(zhuān)欄文章中討論環(huán)境變量帶來(lái)的種種問(wèn)題。

到目前為止,我們已經(jīng)給出了一大堆常見(jiàn) C 函數(shù),這些函數(shù)容易引起緩沖區(qū)溢出問(wèn)題。當(dāng)然,還有許多函數(shù)有相同的問(wèn)題。特別是,注意第三方 COTS 軟件。不要設(shè)想關(guān)于其他人軟件行為的任何事情。還要意識(shí)到我們沒(méi)有仔細(xì)檢查每個(gè)平臺(tái)上的每個(gè)常見(jiàn)庫(kù)(我們不想做那一工作),并且還可能存在其它有問(wèn)題的調(diào)用。

即使我們檢查了每個(gè)常見(jiàn)庫(kù)的各個(gè)地方,如果我們?cè)噲D聲稱(chēng)已經(jīng)列出了將在任何時(shí)候遇到的所有問(wèn)題,則您應(yīng)該持非常非常懷疑的態(tài)度。我們只是想給您起一個(gè)頭。其余全靠您了。

靜態(tài)和動(dòng)態(tài)測(cè)試工具

我們將在以后的專(zhuān)欄文章中更加詳細(xì)地介紹一些脆弱性檢測(cè)的工具,但現(xiàn)在值得一提的是兩種已被證明能有效幫助找到和去除緩沖區(qū)溢出問(wèn)題的掃描工具。這兩個(gè)主要類(lèi)別的分析工具是靜態(tài)工具(考慮代碼但永不運(yùn)行)和動(dòng)態(tài)工具(執(zhí)行代碼以確定行為)。

可以使用一些靜態(tài)工具來(lái)查找潛在的緩沖區(qū)溢出問(wèn)題。很糟糕的是,沒(méi)有一個(gè)工具對(duì)一般公眾是可用的!許多工具做得一點(diǎn)也不比自動(dòng)化 grep 命令多,可以運(yùn)行它以找到源代碼中每個(gè)有問(wèn)題函數(shù)的實(shí)例。由于存在更好的技術(shù),這仍然是高效的方式將幾萬(wàn)行或幾十萬(wàn)行的大程序縮減到只有數(shù)百個(gè)“潛在的問(wèn)題”。(在以后的專(zhuān)欄文章中,將演示一個(gè)基于這種方法的、草草了事的掃描工具,并告訴您有關(guān)如何構(gòu)建它的想法。)

較好的靜態(tài)工具利用以某些方式表示的數(shù)據(jù)流信息來(lái)斷定哪個(gè)變量會(huì)影響到其它哪個(gè)變量。用這種方法,可以丟棄來(lái)自基于 grep 的分析的某些“假肯定”。David Wagner 在他的工作中已經(jīng)實(shí)現(xiàn)了這樣的方法(在“Learning the basics of buffer overflows”中描述;請(qǐng)參閱 參考資料),在 Reliable Software Technologies 的研究人員也已實(shí)現(xiàn)。當(dāng)前,數(shù)據(jù)流相關(guān)方法的問(wèn)題是它當(dāng)前引入了假否定(即,它沒(méi)有標(biāo)志可能是真正問(wèn)題的某些調(diào)用)。

第二類(lèi)方法涉及動(dòng)態(tài)分析的使用。動(dòng)態(tài)工具通常把注意力放在代碼運(yùn)行時(shí)的情況,查找潛在的問(wèn)題。一種已在實(shí)驗(yàn)室使用的方法是故障注入。這個(gè)想法是以這樣一種方式來(lái)檢測(cè)程序:對(duì)它進(jìn)行實(shí)驗(yàn),運(yùn)行“假設(shè)”游戲,看它會(huì)發(fā)生什么。有一種故障注入工具 ― FIST(請(qǐng)參閱 參考資料)已被用來(lái)查找可能的緩沖區(qū)溢出脆弱性。

最終,動(dòng)態(tài)和靜態(tài)方法的某些組合將會(huì)給您的投資帶來(lái)回報(bào)。但在確定最佳組合方面,仍然有許多工作要做。

Java 和堆棧保護(hù)可以提供幫助

堆棧搗毀是最?lèi)毫拥囊环N緩沖區(qū)溢出攻擊,特別是,當(dāng)在特權(quán)模式下?lián)v毀了堆棧。這種問(wèn)題的優(yōu)秀解決方案是非可執(zhí)行堆棧。通常,利用代碼是在程序堆棧上編寫(xiě),并在那里執(zhí)行的。(我們將在下一篇專(zhuān)欄文章中解釋這是如何做到的。)獲取許多操作系統(tǒng)(包括 Linux 和 Solaris)的非可執(zhí)行堆棧補(bǔ)丁是可能的。(某些操作系統(tǒng)甚至不需要這樣的補(bǔ)??;它們本身就帶有。)

非可執(zhí)行堆棧涉及到一些性能問(wèn)題。(沒(méi)有免費(fèi)的午餐。)此外,在既有堆棧溢出又有堆溢出的程序中,它們易出問(wèn)題??梢岳枚褩R绯鍪钩绦蛱D(zhuǎn)至利用代碼,該代碼被放置在堆上。沒(méi)有實(shí)際執(zhí)行堆棧中的代碼,只有堆中的代碼。

當(dāng)然,另一種選項(xiàng)是使用類(lèi)型安全的語(yǔ)言,譬如 Java。較溫和的措施是獲取對(duì) C 程序中進(jìn)行數(shù)組邊界檢查的編譯器。對(duì)于 gcc 存在這樣的工具。這種技術(shù)可以防止所有緩沖區(qū)溢出,堆和堆棧。不利的一面是,對(duì)于那些大量使用指針、速度是至關(guān)重要的程序,這種技術(shù)可能會(huì)影響性能。但是在大多數(shù)情況下,該技術(shù)運(yùn)行得非常好。

Stackguard 工具實(shí)現(xiàn)了比一般性邊界檢查更為有效的技術(shù)。它將一些數(shù)據(jù)放在已分配數(shù)據(jù)堆棧的末尾,并且以后會(huì)在緩沖區(qū)溢出可能發(fā)生前,查看這些數(shù)據(jù)是否仍然在那里。這種模式被稱(chēng)之為“金絲雀”。(威爾士的礦工將 金絲雀放在礦井內(nèi)來(lái)顯示危險(xiǎn)的狀況。當(dāng)空氣開(kāi)始變得有毒時(shí),金絲雀會(huì)昏倒,使礦工有足夠時(shí)間注意到并逃離。)

Stackguard 方法不如一般性邊界檢查安全,但仍然相當(dāng)有用。Stackguard 的主要缺點(diǎn)是,與一般性邊界檢查相比,它不能防止堆溢出攻擊。一般來(lái)講,最好用這樣一個(gè)工具來(lái)保護(hù)整個(gè)操作系統(tǒng),否則,由程序調(diào)用的不受保護(hù)庫(kù)(譬如,標(biāo)準(zhǔn)庫(kù))可以仍然為基于堆棧的利用代碼攻擊打開(kāi)了大門(mén)。

類(lèi)似于 Stackguard 的工具是內(nèi)存完整性檢查軟件包,譬如,Rational 的 Purify。這類(lèi)工具甚至可以保護(hù)程序防止堆溢出,但由于性能開(kāi)銷(xiāo),這些工具一般不在產(chǎn)品代碼中使用。

結(jié)束語(yǔ)

下面表格總結(jié)了一些編程構(gòu)造,我們建議你小心使用或避免使用它們。如果要想代碼健壯,最好有一定容錯(cuò)處理最好,比如之前給大家分享過(guò)的《斷言》。

函數(shù) 嚴(yán)重性 解決方案
gets 最危險(xiǎn) 使用 fgets(buf, size, stdin)。這幾乎總是一個(gè)大問(wèn)題!
strcpy 很危險(xiǎn) 改為使用 strncpy。
strcat 很危險(xiǎn) 改為使用 strncat。
sprintf 很危險(xiǎn) 改為使用 snprintf,或者使用精度說(shuō)明符。
scanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
sscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
fscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
vfscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
vsprintf 很危險(xiǎn) 改為使用 vsnprintf,或者使用精度說(shuō)明符。
vscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
vsscanf 很危險(xiǎn) 使用精度說(shuō)明符,或自己進(jìn)行解析。
streadd 很危險(xiǎn) 確保分配的目的地參數(shù)大小是源參數(shù)大小的四倍。
strecpy 很危險(xiǎn) 確保分配的目的地參數(shù)大小是源參數(shù)大小的四倍。
strtrns 危險(xiǎn) 手工檢查來(lái)查看目的地大小是否至少與源字符串相等。
realpath 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 分配緩沖區(qū)大小為 MAXPATHLEN。同樣,手工檢查參數(shù)以確保輸入?yún)?shù)不超過(guò) MAXPATHLEN。
syslog 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
getopt 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
getopt_long 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
getpass 很危險(xiǎn)(或稍小,取決于實(shí)現(xiàn)) 在將字符串輸入傳遞給該函數(shù)之前,將所有字符串輸入截成合理的大小。
getchar 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
fgetc 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
read 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
bcopy 中等危險(xiǎn) 如果在循環(huán)中使用該函數(shù),確保檢查緩沖區(qū)邊界。
fgets 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
memcpy 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
snprintf 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
strccpy 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
strcadd 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
strncpy 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
getchar 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。
vsnprintf 低危險(xiǎn) 確保緩沖區(qū)大小與它所說(shuō)的一樣大。

推薦閱讀:

gcc和g++是什么,有什么區(qū)別?

預(yù)處理 #pragma 命令詳解

ARM 編譯工具keil 和 IAR 命令行編譯和下載


關(guān)注 微信公眾號(hào)『strongerHuang』,后臺(tái)回復(fù)“1024”查看更多內(nèi)容,回復(fù)“加群”按規(guī)則加入技術(shù)交流群。


長(zhǎng)按前往圖中包含的公眾號(hào)關(guān)注

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉