當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式微處理器
[導(dǎo)讀]所謂的數(shù)組越界,簡(jiǎn)單地講就是指數(shù)組下標(biāo)變量的取值超過(guò)了初始定義時(shí)的大小,導(dǎo)致對(duì)數(shù)組元素的訪問(wèn)出現(xiàn)在數(shù)組的范圍之外,這類錯(cuò)誤也是C語(yǔ)言程序中最常見(jiàn)的錯(cuò)誤之一。在C語(yǔ)言中,數(shù)組必須是靜態(tài)的。換而言之,數(shù)組的大小必須在程序運(yùn)行前就確定下來(lái)。由于C語(yǔ)言并不具有類似Java等語(yǔ)言中現(xiàn)有的靜...

所謂的數(shù)組越界,簡(jiǎn)單地講就是指數(shù)組下標(biāo)變量的取值超過(guò)了初始定義時(shí)的大小,導(dǎo)致對(duì)數(shù)組元素的訪問(wèn)出現(xiàn)在數(shù)組的范圍之外,這類錯(cuò)誤也是C語(yǔ)言程序中最常見(jiàn)的錯(cuò)誤之一。

在C語(yǔ)言中,數(shù)組必須是靜態(tài)的。換而言之,數(shù)組的大小必須在程序運(yùn)行前就確定下來(lái)。由于C語(yǔ)言并不具有類似Java等語(yǔ)言中現(xiàn)有的靜態(tài)分析工具的功能,可以對(duì)程序中數(shù)組下標(biāo)取值范圍進(jìn)行嚴(yán)格檢查,一旦發(fā)現(xiàn)數(shù)組上溢或下溢,都會(huì)因拋出異常而終止程序。也就是說(shuō),C語(yǔ)言并不檢驗(yàn)數(shù)組邊界,數(shù)組的兩端都有可能越界,從而使其他變量的數(shù)據(jù)甚至程序代碼被破壞。

因此,數(shù)組下標(biāo)的取值范圍只能預(yù)先推斷一個(gè)值來(lái)確定數(shù)組的維數(shù),而檢驗(yàn)數(shù)組的邊界是程序員的職責(zé)。

一般情況下,數(shù)組的越界錯(cuò)誤主要包括兩種:數(shù)組下標(biāo)取值越界與指向數(shù)組的指針的指向范圍越界。

數(shù)組下標(biāo)取值越界

數(shù)組下標(biāo)取值越界主要是指訪問(wèn)數(shù)組的時(shí)候,下標(biāo)的取值不在已定義好的數(shù)組的取值范圍內(nèi),而訪問(wèn)的是無(wú)法獲取的內(nèi)存地址。例如,對(duì)于數(shù)組?int a[3],它的下標(biāo)取值范圍是[0,2](即a[0]、a[1] 與 a[2])。如果我們的取值不在這個(gè)范圍內(nèi)(如 a[3]),就會(huì)發(fā)生越界錯(cuò)誤。示例代碼如下所示:

1int?a[3];
2int?i=0;
3for(i=0;i<4;i )
4{
5????a[i]?=?i;
6}
7for(i=0;i<4;i )
8{
9????printf("a[%d]=%d\n",i,a[i]);
10}
很顯然,在上面的示例程序中,訪問(wèn) a[3] 是非法的,將會(huì)發(fā)生越界錯(cuò)誤。因此,我們應(yīng)該將上面的代碼修改成如下形式:

1int?a[3];
2int?i=0;
3for(i=0;i<3;i )
4{
5????a[i]?=?i;
6}
7for(i=0;i<3;i )
8{
9????printf("a[%d]=%d\n",i,a[i]);
10}

指向數(shù)組的指針的指向范圍越界

指向數(shù)組的指針的指向范圍越界是指定義數(shù)組時(shí)會(huì)返回一個(gè)指向第一個(gè)變量的頭指針,對(duì)這個(gè)指針進(jìn)行加減運(yùn)算可以向前或向后移動(dòng)這個(gè)指針,進(jìn)而訪問(wèn)數(shù)組中所有的變量。但在移動(dòng)指針時(shí),如果不注意移動(dòng)的次數(shù)和位置,會(huì)使指針指向數(shù)組以外的位置,導(dǎo)致數(shù)組發(fā)生越界錯(cuò)誤。

下面的示例代碼,就是移動(dòng)指針時(shí)沒(méi)有考慮到移動(dòng)的次數(shù)和數(shù)組的范圍,從而使程序訪問(wèn)了數(shù)組以外的存儲(chǔ)單元。

1int?i;
2int?*p;
3int?a[5];
4/*數(shù)組a的頭指針賦值給指針p*/
5p=a;
6for(i=0;i<10;i )
7{
8????/*指針p指向的變量*/
9????*p=i 10;
10????/*指針p下一個(gè)變量*/
11????p ;
12}
在上面的示例代碼中,for循環(huán)會(huì)使指針p向后移動(dòng)10次,并且每次向指針指向的單元賦值。但是,這里數(shù)組a的下標(biāo)取值范圍是?[0,4](即?a[0]、a[1]、a[2]、a[3] 與 a[4])。因此,后5次的操作會(huì)對(duì)未知的內(nèi)存區(qū)域賦值,而這種向內(nèi)存未知區(qū)域賦值的操作會(huì)使系統(tǒng)發(fā)生錯(cuò)誤。

正確的操作,應(yīng)該是指針移動(dòng)的次數(shù)與數(shù)組中的變量個(gè)數(shù)相同,如下面的代碼所示:

1int?i;
2int?*p;
3int?a[5];
4/*數(shù)組a的頭指針賦值給指針p*/
5p=a;
6for(i=0;i<5;i )
7{
8????/*指針p指向的變量*/
9????*p=i 10;
10????/*指針p下一個(gè)變量*/
11????p ;
12}
為了加深大家對(duì)數(shù)組越界的了解,下面通過(guò)一段完整的數(shù)組越界示例來(lái)演示編程中數(shù)組越界將會(huì)導(dǎo)致哪些問(wèn)題。

1#define?PASSWORD?"123456"
2int?Test(char?*str)
3
{
4????int?flag;
5????char?buffer[7];
6????flag=strcmp(str,PASSWORD);
7????strcpy(buffer,str);
8????return?flag;
9}
10int?main(void)
11
{
12????int?flag=0;
13????char?str[1024];
14????while(1)
15????{
16????????printf("請(qǐng)輸入密碼:??");
17????????scanf"%s",str);
18????????flag?=?Test(str);
19????????if(flag)
20????????{
21????????????printf("密碼錯(cuò)誤!\n");
22????????}
23????????????else
24????????????{
25????????????????printf("密碼正確!\n");
26????????????}
27????}
28????return?0;
29}
上面的示例代碼模擬了一個(gè)密碼驗(yàn)證的例子,它將用戶輸入的密碼與宏定義中的密碼123456進(jìn)行比較。很顯然,本示例中最大的設(shè)計(jì)漏洞就在于 Test() 函數(shù)中的?strcpy(buffer,str)?調(diào)用。

由于程序?qū)⒂脩糨斎氲淖址獠粍?dòng)地復(fù)制到 Test() 函數(shù)的數(shù)組?char buffer[7]?中。因此,當(dāng)用戶的輸入大于 7 個(gè)字符的緩沖區(qū)尺寸時(shí),就會(huì)發(fā)生數(shù)組越界錯(cuò)誤,這也就是大家所謂的緩沖區(qū)溢出Buffer overflow?漏洞。

但是要注意,如果這個(gè)時(shí)候我們根據(jù)緩沖區(qū)溢出發(fā)生的具體情況填充緩沖區(qū),不但可以避免程序崩潰,還會(huì)影響到程序的執(zhí)行流程,甚至?xí)尦绦蛉?zhí)行緩沖區(qū)里的代碼。示例運(yùn)行結(jié)果為:

1請(qǐng)輸入密碼:12345
2密碼錯(cuò)誤!
3請(qǐng)輸入密碼:123456
4密碼正確!
5請(qǐng)輸入密碼:1234567
6密碼正確!
7請(qǐng)輸入密碼:aaaaaaa
8密碼正確!
9請(qǐng)輸入密碼:0123456
10密碼錯(cuò)誤!
11請(qǐng)輸入密碼:
在示例代碼中,flag 變量實(shí)際上是一個(gè)標(biāo)志變量,其值將決定著程序是進(jìn)入密碼錯(cuò)誤的流程(非 0)還是“密碼正確”的流程(0)。當(dāng)我們輸入錯(cuò)誤的字符串1234567或者aaaaaaa,程序也都會(huì)輸出“密碼正確”。但在輸入0123456的時(shí)候,程序卻輸出“密碼錯(cuò)誤”,這究竟是為什么呢?

其實(shí),原因很簡(jiǎn)單。當(dāng)調(diào)用 Test() 函數(shù)時(shí),系統(tǒng)將會(huì)給它分配一片連續(xù)的內(nèi)存空間,而變量?char buffer[7]?與?int flag?將會(huì)緊挨著進(jìn)行存儲(chǔ),用戶輸入的字符串將會(huì)被復(fù)制進(jìn) buffer[7] 中。如果這個(gè)時(shí)候,我們輸入的字符串?dāng)?shù)量超過(guò) 6 個(gè)(注意,有字符串截?cái)喾菜阋粋€(gè)),那么超出的部分將破壞掉與它緊鄰著的 flag 變量的內(nèi)容。

當(dāng)輸入的密碼不是宏定義的123456時(shí),字符串比較將返回 1 或 -1。我們都知道,內(nèi)存中的數(shù)據(jù)按照 4 字節(jié)(DWORD)逆序存儲(chǔ),所以當(dāng) flag 為 1 時(shí),在內(nèi)存中存儲(chǔ)的是0x01000000。如果我們輸入包含 7 個(gè)字符的錯(cuò)誤密碼,如aaaaaaa,那么字符串截?cái)喾?0x00 將寫入 flag 變量,這樣溢出數(shù)組的一個(gè)字節(jié) 0x00 將恰好把逆序存放的 flag 變量改為?0x00000000。在函數(shù)返回后,一旦 main 函數(shù)的 flag 為 0,就會(huì)輸出“密碼正確”。這樣,我們就用錯(cuò)誤的密碼得到了正確密碼的運(yùn)行效果。

而對(duì)于0123456,因?yàn)樵谶M(jìn)行字符串的大小比較時(shí),它小于123456,flag的值是 -1,在內(nèi)存中將按照補(bǔ)碼存放負(fù)數(shù),所以實(shí)際存儲(chǔ)的不是?0x01000000?而是?0xffffffff。那么字符串截?cái)嗪蠓?0x00 淹沒(méi)后,變成?0x00ffffff,還是非 0,所以沒(méi)有進(jìn)入正確分支。

其實(shí),本示例只是用一個(gè)字節(jié)淹沒(méi)了鄰接變量,導(dǎo)致程序進(jìn)入密碼正確的處理流程,使設(shè)計(jì)的驗(yàn)證功能失效。

盡量顯式地指定數(shù)組的邊界

在 C 語(yǔ)言中,為了提高運(yùn)行效率,給程序員更大的空間,為指針操作帶來(lái)更多的方便,C 語(yǔ)言內(nèi)部本身不檢查數(shù)組下標(biāo)表達(dá)式的取值是否在合法范圍內(nèi),也不檢查指向數(shù)組元素的指針是不是移出了數(shù)組的合法區(qū)域。因此,在編程中使用數(shù)組時(shí)就必須格外謹(jǐn)慎,在對(duì)數(shù)組進(jìn)行讀寫操作時(shí)都應(yīng)當(dāng)進(jìn)行相應(yīng)的檢查,以免對(duì)數(shù)組的操作超過(guò)數(shù)組的邊界,從而發(fā)生緩沖區(qū)溢出漏洞。

要避免程序因數(shù)組越界所發(fā)生的錯(cuò)誤,首先就需要從數(shù)組的邊界定義開(kāi)始。盡量顯式地指定數(shù)組的邊界,即使它已經(jīng)由初始化值列表隱式指定。示例代碼如下所示:

1int?a[]={1,2,3,4,5,6,7,8,9,10};
很顯然,對(duì)于上面的數(shù)組 a[],雖然編譯器可以根據(jù)始化值列表來(lái)計(jì)算出數(shù)組的長(zhǎng)度。但是,如果我們顯式地指定該數(shù)組的長(zhǎng)度,例如:

1int?a[10]={1,2,3,4,5,6,7,8,9,10};
它不僅使程序具有更好的可讀性,并且大多數(shù)編譯器在數(shù)組長(zhǎng)度小于初始化值列表的長(zhǎng)度時(shí)還會(huì)發(fā)生相應(yīng)警告。

當(dāng)然,也可以使用宏的形式來(lái)顯式指定數(shù)組的邊界(實(shí)際上,這也是最常用的指定方法),如下面的代碼所示:

1#define?MAX?10
2
3int?a[MAX]={1,2,3,4,5,6,7,8,9,10};
除此之外,在 C99 標(biāo)準(zhǔn)中,還允許我們使用單個(gè)指示符為數(shù)組的兩段“分配”空間,如下面的代碼所示:

1int?a[MAX]={1,2,3,4,5,[MAX-5]=6,7,8,9,10};
在上面的?a[MAX]數(shù)組中,如果 MAX 大于 10,數(shù)組中間將用 0 值元素進(jìn)行填充(填充的個(gè)數(shù)為?MAX-10,并從 a[5] 開(kāi)始進(jìn)行 0 值填充);如果 MAX 小于 10,[MAX-5]之前的 5 個(gè)元素(1,2,3,4,5)中將有幾個(gè)被[MAX-5]之后的 5 個(gè)元素(6,7,8,9,10)所覆蓋,示例代碼如下所示:

1#define?MAX?10
2#define?MAX1?15
3#define?MAX2?6
4int?main(void)
5{
6????int?a[MAX]={1,2,3,4,5,[MAX-5]=6,7,8,9,10};
7????int?b[MAX1]={1,2,3,4,5,[MAX1-5]=6,7,8,9,10};
8????int?c[MAX2]={1,2,3,4,5,[MAX2-5]=6,7,8,9,10};
9????int?i=0;
10????int?j=0;
11????int?z=0;
12????printf("a[MAX]:\n");
13????for(i=0;i14????{
15????????printf("a[%d]=%d?",i,a[i]);
16????}
17????printf("\nb[MAX1]:\n");
18????for(j=0;j19????{
20????????printf("b[%d]=%d?",j,b[j]);
21????}
22????printf("\nc[MAX2]:\n");
23????for(z=0;z24????{
25????????printf("c[%d]=%d?",z,c[z]);
26????}
27????printf("\n");
28????return?0;
29}
運(yùn)行結(jié)果為:

1a[MAX]:
2a[0]=1?a[1]=2?a[2]=3?a[3]=4?a[4]=5?a[5]=6?a[6]=7?a[7]=8?a[8]=9?a[9]=10
3b[MAX1]:
4b[0]=1?b[1]=2?b[2]=3?b[3]=4?b[4]=5?b[5]=0?b[6]=0?b[7]=0?b[8]=0?b[9]=0?b[10]=6?b[11]=7?b[12]=8?b[13]=9?b[14]=10
5c[MAX2]:
6c[0]=1?c[1]=6?c[2]=7?c[3]=8?c[4]=9?c[5]=10

對(duì)數(shù)組做越界檢查,確保索引值位于合法的范圍之內(nèi)

要避免數(shù)組越界,除了上面所闡述的顯式指定數(shù)組的邊界之外,還可以在數(shù)組使用之前進(jìn)行越界檢查,檢查數(shù)組的界限和字符串(也以數(shù)組的方式存放)的結(jié)束,以保證數(shù)組索引值位于合法的范圍之內(nèi)。例如,在寫處理數(shù)組的函數(shù)時(shí),一般應(yīng)該有一個(gè)范圍參數(shù);在處理字符串時(shí)總檢查是否遇到空字符‘\0’。

來(lái)看下面一段代碼示例:

1#define?ARRAY_NUM?10
2int?*TestArray(int?num,int?value)
3
{
4????int?*arr=NULL;
5????arr=(int?*)malloc(sizeof(int)*ARRAY_NUM);
6????if(arr!=NULL)
7????{
8????????arr[num]=value;
9????}
10????else
11????{
12????????/*處理arr==NULL*/
13????}
14????return?arr;
15}
從上面的int*TestArray(int num,int value)函數(shù)中不難看出,其中存在著一個(gè)很明顯的問(wèn)題,那就是無(wú)法保證 num 參數(shù)是否越界(即當(dāng)num>=ARRAY_NUM的情況)。因此,應(yīng)該對(duì) num 參數(shù)進(jìn)行越界檢查,示例代碼如下所示:

1int?*TestArray(int?num,int?value)
2
{
3????int?*arr=NULL;
4????/*越界檢查(越上界)*/
5????if(num 6????{
7????????arr=(int?*)malloc(sizeof(int)*ARRAY_NUM);
8????????if(arr!=NULL)
9????????{
10????????????arr[num]=value;
11????????}
12????????else
13????????{
14????????????/*處理arr==NULL*/
15????????}
16????}
17????return?arr;
18}
這樣通過(guò)if(num語(yǔ)句進(jìn)行越界檢查,從而保證 num 參數(shù)沒(méi)有越過(guò)這個(gè)數(shù)組的上界?,F(xiàn)在看起來(lái),TestArray() 函數(shù)應(yīng)該沒(méi)什么問(wèn)題,也不會(huì)發(fā)生什么越界錯(cuò)誤。

但是,如果仔細(xì)檢查,TestArray() 函數(shù)仍然還存在一個(gè)致命的問(wèn)題,那就是沒(méi)有檢查數(shù)組的下界。由于這里的 num 參數(shù)類型是 int 類型,因此可能為負(fù)數(shù)。如果 num 參數(shù)所傳遞的值為負(fù)數(shù),將導(dǎo)致在 arr 所引用的內(nèi)存邊界之外進(jìn)行寫入。

當(dāng)然,你可以通過(guò)向if(num語(yǔ)句里面再加一個(gè)條件進(jìn)行測(cè)試,如下面的代碼所示:

1if(num>=0
嵌入式ARM

掃描二維碼,關(guān)注更多精彩內(nèi)容

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

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

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

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

關(guān)鍵字: 汽車 人工智能 智能驅(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ā)表演講稱,數(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)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

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