1. gzyueqian
      18529173453
      首頁 > 新聞中心 > > 正文

      指針、結構體、聯(lián)合體的安全規(guī)范

      更新時間: 2007-02-08 23:05:18來源: 粵嵌教育瀏覽量:1130


        指針賦予了C編程的靈活性;結構體使得C程序整齊而緊湊;聯(lián)合體在某些要求注重效率的場合有精彩的表現(xiàn),這三個要素是C語言的精華。

        然而,精華并不意味著完美,C語言在賦予程序員足夠靈活性的同時,也給了程序員很多犯錯誤的機會。所以有必要關注指針、結構體和聯(lián)合體的實現(xiàn)細節(jié),從而保障程序的安全性。

        在此.部分介紹《MISRA—C:2004》中與指針相關的部分規(guī)則,第二部分講解結構體和聯(lián)合體的操作規(guī)范。下文中凡是未加特殊說明的都是強制(required)規(guī)則,個別推薦(advisory)規(guī)則加了“推薦”標示。

      1 指針的安全規(guī)范

        《MISRA—C:2004》關于指針的規(guī)范主要分為三個部分:指針的類型轉換規(guī)則、指針運算的規(guī)則和指針的有效性規(guī)則。

      1.1 指針的類型轉換

        指針類型轉換是個高風險的操作,所以應該盡量避免進行這個操作。MISRA—C對其中可能造成嚴重錯誤的情況作了嚴格的限定,選擇其中兩條作簡要分析。

        規(guī)則11.4(推薦):指向不同數據類型的指針之間不能相互轉換。

        思考如下程序:

      uint8_t*pl;

      uint32)_t*p2;

      p2=(uint32_t*)pl;

        /*注:uint8_t表示8位無符號整型,uint3_t表示32位無符號整型。*/

        程序員希望將從p1單元開始的4個字節(jié)組成一個32付的整型來參與運算。

        如果CPU允許各種數據對象存放在任意的存儲單元,則以上轉換沒有問題。但某些CPU對某種(些)數據類型加強了對齊限制,要求這些數據對象占用一定的地址空間,比如某些字節(jié)尋址的CPU會要求32位(4字節(jié))整型存放在4的整倍數地址上。在這個前提下.思考程序中的指針轉換:假設pl一開始指向的是0x00O3單元(對uint8_t型的整型沒有對齊要求),則執(zhí)行一行強制轉換后,p2到底指向哪個單元就無法預料了。

        規(guī)則1 1.5:指針轉換過程中不允許丟失指針的const、volatile屬性。按如下定義指針:

      uIntl6一t x;

      uint16_t*const cpi=&x; /*const指針*/

      uintl6_t*const *pcpi; /*指向const指針的指針*/

      const uintl6_t* *ppci; /*指向const整型指針的指針*/

      uIntl6_t* *ppi ;

      const uint16_t *pci; /*指向const整型的指針*/

      volatik uint16_t *pvi; /*指向volatile整型的指針*/

      uintl6_t *pi;


      則以下指針轉換是允許的:

      pl=cpi;

      以下指針轉換是不允許的:

      pi=(umtl6_t*)pci;

      pi=(uintl6_t*)pvil

      ppi=(uintl6_t* *)pcpi;

      ppi=(uintl6_I**)ppci+

        以上非法指針類型轉換將會丟失const或者volatile類型。丟失const屬性,將有可能導致在對只讀內容進行寫操作時,編譯器不會發(fā)出警告,編譯器將不對具有volatile屬性的變量作優(yōu)化;丟失volatile屬性,編譯器的優(yōu)化可能導致程序員預先設計的硬件時序操作失效,這樣的錯誤很難發(fā)現(xiàn)。關于const和volatile關鍵字的詳細作用,讀者可參考ISOC獲取更多信息。

      1.2 指針的運算

        ISOC標準中,對指向數組成員的指針運算(包括算術運算、比較等)做了規(guī)范定義,除此以外的指針運算屬于未定義(undefined)范圍,具體實現(xiàn)有賴于具體編譯器,其安全性無法得到保障,MISRA—C中對指針運算的合法范圍做了如下限定。

        規(guī)則17.1:只有指向數組的指針才允許進行算術運算①。

        規(guī)則17 2:只有指向同一個數組的兩個指針才允許相減 ②。

        規(guī)則17 3:只有指向同一個數組的兩個指針才允許用>,>=,<,<=等關系運算符進行比較。

        為了盡可能減少直接進行指針運算帶來的隱患,尤其是程序動態(tài)運行時可能發(fā)生的數組越界等問題,MISRA—C對指針運算作了更為嚴格的規(guī)定。規(guī)則17 4:只允許用數組索引做指針運算。按如下方式定義數組和指針:

      uint8_t a[10];

      uint8_t *p;

        則*(p+5)=O是不允許的.而p[5]=O則是允許的,盡管就這段程序而言,二者等價。

        以下給出一段程序,讀者可參照相應程序行的注釋,細細品味上述規(guī)則的含義。

      void my_fn(uInt*_t*p1.uint8_t p2[]){

      ①其實此處的算術運算僅限定于指針加減某個整數.比如ppoint=point一5.ppoint++等。0兩個指針可指向不同的散組成員。

      uint8_t index=0;

      uint8_t *p3

      uint8_t *p4;

      *pl=O;

      p1++; /*不允許,pl不是指向數組的指針*/

      p1=p1+5;/*不允許,pl不是指向數組的指針*/

      pl[5]=O; /*不允許,p1不是指向數組的指針*/

      p3=&p1[5];/*不允許,pl不是指向數組的指針*/

      p2[0]=O;

      index++;

      index=index+5:

      p2[index]=0; /*允許*/

      *(p2+index)=O; /*不允許*/

      p4=&p2[5]; /*允許*/

      }

      1.3 指針的有效性

        下面介紹《MISRA—C:2004》中關于指針有效性的規(guī)則。

        規(guī)則17 6:動態(tài)分配對象的地址不允許在本對象消亡后傳給另外一個對象。

        這條規(guī)則的實際意義是不允許將棧對象的地址傳給外部作用域的對象。

        請看以下這段程序:

      #include″stdi0.h″

      char*getm(void){

      char p[]=″hello world″;

      return p;

      intmain(){

      char* str=NULL;

      str=getm();

      printf(str);

        程序員希望的輸出結果是″hello world″這個字符串,然而實際運行時,卻出現(xiàn)亂碼(具體內容依賴于編譯環(huán)境)。

        簡單分析一下,由于chat p[]=″hell0 world″這條語句是在棧中分配空間存儲″hell0 world″這個字符串,當函數getm()返回的時候,已分配的空間將會被釋放(但內容并不會被銷毀),而priM(str)涉及系統(tǒng)調用,有數據壓棧,會修改從前分配給數組p[]存儲空間的內容,導致程序無法得到預期的效果。

        倘若將getm()函數體中的char p[]=″hell0 world″程序行改成char*q=″hello world″,則執(zhí)行main( )的時候可以正確輸出″hello world″,這是由于q指向的是靜態(tài)數據區(qū),而非棧中的某個單元。

        所以,數組名是指針不假,但在實現(xiàn)細節(jié)上還是有很大的差異,程序員在使用指針的時候必須慎之又慎。

      2 結構體、聯(lián)合體的安全規(guī)范

        規(guī)則18 4:不允許使用聯(lián)合體。這是一個不太近情理的規(guī)定,在具體闡述為何《MIS—RA—C:2004》如此“痛恨”聯(lián)合體之前,首先需要明確與聯(lián)合體相關的細節(jié):

      ①聯(lián)合體的末尾有多少個填充單元?

      ②聯(lián)合體中的各個成員如何對齊?

      ③多字節(jié)的數據類型高低字節(jié)如何排放順序?

      ④如果包含位字段(bit—field),各位如何排放順序?

        針對細節(jié)3舉個例子。

        程序段2.1

      typedef union{

      uilat32_t word;

      uint8_t bytes[4];

      }word_msg_t;

      unit32_t read_nasg(void){

      word_rnsg_t tmp;

        /*注:tmn bvte[O]對府干tmp.word的高8位,tmp byter[l]對應于

      tmp.WOfO的次高8位,依次類推。*/

      tmp.bytes[O]=read_byte();

      tmp.bytes[1]=read_byte();

      tmp.bytes[2]=read_byte();

      tmp.bytes[3]=read_byte();

      retlarn(trap.word);

      }

        以上代碼格式在各種通信協(xié)議中使用的頻率很高,接收端接收到的數據一般都以字節(jié)為單位存放,主控程序需要根據相應的協(xié)議將接收到的多個字節(jié)進行組合。為了實現(xiàn)相同的功能,《MISRA-C:2004》推薦了read_msg()函數的另外一種寫法。

        程序段2.2

      uint32_trcad_msg(void){

      uint32_t word;

      Word=((unit32_t)read_byte())<<24;

      word=word│(((unit32_t)read_byte())<<16);

      word=word│(((unit32_t)read_byted_byte<<8);

      word=word│(((unit32_t)read_byte());

      return(word):

      }

        無論從程序的清晰程度還是執(zhí)行效率來講,程序段2.1都優(yōu)于程序段2.2。然而,程序段2.1在Intel 80x86/Pentlurn體系(1ittle—endian,存儲多字節(jié)整數的時候低字節(jié)存放在低地址,高字節(jié)存放在高地址)CPU中和在Mo—torola 68K體系(big—endian,存儲多字節(jié)整數的時候高字節(jié)存放在低地址,低字節(jié)存放在高地址)cPu中的執(zhí)行結果完全不一樣。假設read_byte()函數返回的數據依次是0x01、0x02、0x03和0x04,則在Intel體系中,程序段2.1 中read_msg()函數的返回值是0x432l;在Motorola體系中,read_msg()的返回值是0x1234。

        無論在Intel體系還是Motorola體系中,程序段2.2中read_msg()的返回值都是0x1 234。

        以上是聯(lián)合體中多字節(jié)整型字節(jié)排放順序不定導致漏洞的一個例子。倘若不明確聯(lián)合體末尾填充的細節(jié),或者不清楚聯(lián)合體成員的對齊方式,或者不注意聯(lián)合體中位字段成員的位排列次序,都有可能導致錯誤。作為將安全性放在位的C標準,MlSRA—C禁止使用聯(lián)合體并非不可理喻。

        然而,聯(lián)合體畢竟是C語言的一個重要元素,所以MISRA—C主張禁止使用聯(lián)合體的同時,也為效率和資源要求比較苛刻的情況開了一扇門,程序員在明確聯(lián)合體各個實現(xiàn)細節(jié)的前提下,在萬不得已的時候,仍可謹慎使用聯(lián)合體,在不同體系的CPU間移植程序的時候要注意做相應的修改。

        此外,《MISRA—C:2004》中也對結構體和聯(lián)合體的編程風格作了限定。

        規(guī)則18 1:所有結構體和聯(lián)合體的定義必須保證完整性。

        由于涉及ISOC中類型定義完整性等概念,礙于篇幅的原因,此處就不再贅述,讀者可以參閱《MISRA-C:2004》一書和ISOC標準以了解更多信息,完善自己的編程風格。

      3 小 結

        總而言之,對于C程序中為靈活的指針、結構體和聯(lián)合體,程序員不僅僅要關注其定義和操作的一般方法,更要注重實現(xiàn)的細節(jié)。由于指針、聯(lián)合體等的功能性錯誤一般都可以逃過編譯器的檢查,所以稍有疏忽,就可能導致程序在運行的時候出現(xiàn)嚴重錯誤,程序員必須以嚴謹甚至苛刻的態(tài)度對待指針、結構體和聯(lián)合體。

      免費預約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 日韩精品旡码一区二区三区 | 婷婷中文字幕精品一区二区 | 午夜性色福利免费视频在线播放 | 日本一区不卡高清更新区 | 日本一二区中文字幕在线 | 亚洲最大中文字幕 |