野指針危害真的很大嗎?
設想,你家裡有個物體,不知什麼時候突然出現,也不知什麼時候突然消失。會把你的東西亂挪位置,還時不時打碎個瓶子。
這個物體,在計算機的世界叫野指針。在現實世界,叫貓。前幾天剛跟同事一起解了個dangling pointer+double free的bug。那解的簡直痛苦。前後一共花了一整個月,一個人全職、三個人協助去分析。
而這還算運氣好的了,居然解了。
要不是前人有先見之明,在free的時候zap了一下內存(把要被free的內容先用個特殊值填充起來),這bug估計我們到現在也不會有頭緒。
場景是,C++寫的程序,調用某個對象的虛函數時segfault。具體看可以發現是vptr的值被corrupt了。但誰會去寫對象頭裡的vptr呢,肯定是別處有問題。
大家有興趣猜猜實際出錯的方式是怎樣的順序么?
更新:對我們那個bug里malloc()/free()的順序感興趣的同學,請跳傳送門:An example of a dangling pointer + double free bug...把實際生產代碼的抽象層剝離掉,只留下最關鍵的malloc()/free()動作的話,就如上面傳送門所寫的順序一樣。危害非常大!
註:以下系胡編亂造非本人經歷,特此澄清
幾分鐘之前我遭遇的事情。我租的房(合租) 正在家裡上著網 突然有2個人拿鑰匙開門闖進來(寫入) 一問是房東他媽(另外的任務)拿著過期的房間名冊(野指針)給新租客介紹房子(房間我1個月前就租下來了,指向的內存已被釋放並分配給其他任務)如果我正在做冰毒(關鍵操作)有人闖進來(內存被意外修改) 後果不堪設想
不大, 現代操作系統下, 進程都是有獨立的地址空間的.不小心寫出了野指針, 不要說出來, 不要改, 自己默默記住位置. 這就是你下個月獎金的來源.
內存越界型的錯誤調試難度通常較高,因為程序通常不在錯誤發生時崩潰,而是在使用被覆蓋的內容時以一定概率崩潰,此時通常已經離真正發生錯誤的地方很遠。雖然有valgrind一類的設施幫助調試,但這類設施極大幅度增加運行開銷,有時並不可用。
野指針的問題在於,指針指向的地址已經無效了,但是又不是以明顯的NULL指針的形式出現,而偏偏解引用一個非空的無效指針又是一個未定義行為(undefined behavior),也就是不見得一定會導致段錯誤的行為。
換言之,野指針很難定位到是哪裡出現的問題,在哪裡這個指針就失效了,不好查找出錯的原因。所以很多人都會自己封裝一個free宏,在釋放內存的同時將這個指針置NULL。非常大。
幾周前解了個多線程情況下的並發BUG,鎖對數據結構的保護不到位,導致:
競爭條件+野指針+heap corruption。
一個線程調某個數據結構的close函數釋放內存後,另一個線程把已釋放的數據結構給寫了。
BUG難復現,而且程序掛死的地方是不確定的:任何函數調用鏈里----調用到malloc或者free的地方,都可能掛死,也可能不掛死。
程序跑飛了,掛死時打出來的core文件幾乎沒參考價值。
加了一堆的printf,寫了個測試腳本不斷地跑,一部分一部分的排除模塊。
最後,某次問題出現時,那個數據結構close函數的末尾先列印了信息,然後使用那個數據結構的另一個線程列印了退出信息,這個順序不對!
然後我就讓使用這個數據結構的線程在釋放鎖之後立刻sleep了100毫秒,再執行後續代碼。問題變成了必現!
把釋放鎖之後再操作這個數據結構的代碼,挪到釋放鎖之前,問題解決!
自己寫的代碼,都查了兩天,捂臉……危害不大。去年十一假期全組人加班啊。
三倍工資的加班費啊!
是重大利好,哈哈,升職加薪全靠它啊
危害大不大?不大就可以不管么?0.1% 的概率會出現在軟體里也是 bug。所以你可以把這個問題想像成 bug 危害大不大。
http://blog.chinaunix.net/uid-24227137-id-3270110.html
這裡有很詳細的解釋。
指針是個很強大的工具,可是正因為它太強大,所以要操作它不是件易事。
操作不當造成的野指針,甚至會引起系統死機等比較嚴重的後果。
1)如果程序定義了一個指針,就必須要立即讓它指向一個我們設定的空間或者把它設為NULL,如果沒有這麼做,那麼這個指針里的內容是不可預知的,即不知道它 指向內存中的哪個空間(即野指針),它有可能指向的是一個空白的內存區域,可能指向的是已經受保護的區域,甚至可能指向系統的關鍵內存,如果是那樣就糟 了,也許我們後面不小心對指針進行操作就有可能讓系統出現紊亂,死機了。
所以我們必須設定一個空間讓指針指向它,或者把指針設為NULL,這是怎麼樣的一 個原理呢?
如果是建立一個與指針相同類型的空間,實際上是在內存中的空白區域中開闢了這麼一個受保護的內存空間,然後用指針來指向它,那麼指針里的地址就 是這個受保護空間的地址了,而不是不可預知的啦,然後我們就可以通過指針對這個空間進行相應的操作了;如果我們把指針設為NULL,我們在頭文件定義中的 #define NULL 0 可以知道,其實NULL就是表示0,那麼我們讓指針=NULL,實際上就是讓指針=0,如此,指針里的地址(機器數)就被初始化為0了,而內存中地址為0 的內存空間……不用多說也能想像吧,這個地址是特定的,那麼也就不是不可預知的在內存中亂指一氣的野指針了。
還應該注意的是,free和delete只是把指針所指的 內存給釋放掉,但並沒有把指針本身幹掉。指針p被free以後其地址仍然不變(非NULL),只是該地址對應的內存是垃圾,p成了「野指針」。如果此時不 把p設置為NULL,會讓人誤以為p是個合法的指針。
用free或delete釋放了內存之後,就應立即將指針設置為NULL,防止產生「野指針」。
內存 被釋放了,並不表示指針會消亡或者成了NULL指針。
(而且,指針消亡了,也並不表示它所指的內存會被自動釋放,即將指針置為null而不delete,會有內存泄漏,只delete而不置為null會有野指針風險,在定義指針的時候最好一併賦值,比如int * p = xxx;而不要 int *p; p = xxx; 這樣是不安全的做法)
多大危害沒研究過,但是寫進代碼規範的規矩,不要違背。有句話怎麼說的,勿以惡小而為之,出了bug不是坑自己也是坑開發的同行。
沒對象啊
大
不管怎樣,野生的就是好。
野指針就跟野孩子一樣,你說危害大不大?
影響局限於進程內部。。說大不大,說小不小。
野指針有些情況下會出現小概率的崩潰,這個最要命。開發,測試過程中一切OK,上線運行了很久崩潰了,你說要命不要命!
大概就是這種傢伙吧
其實個人覺得不是很大,指針也是數據,首先如果是局部的,不置空也沒關係反正用不到了,如果是全局的我覺得用的時候釋放了可能也會立馬再次新賦值,如果不是那肯定需要重置為null,這也只是方便你的判斷是否需要賦值,如果你再次用不到,那麼就完全不影響程序的健壯性。有的可能比較複雜不一定開始就初始化,那你在某個地方用的時候會判斷是否為空,然後給它賦值。就像很多做邏輯判斷的bool,初始也會有值,如果沒值,那你就看系統給的初始值,區別就是指針如果沒初始可能會導致崩潰。當然還有的就是不方便調試,指針崩潰,可能引用錯誤不固定值,可能崩潰的地方很怪異,找不到出錯地方。
所以我覺得野指針對c,c++來說很低級的錯誤,養成習慣。或許也是我遇到的少。感覺很蛋疼的事,不小心把別的對象給他了,大小相等,不等的,遇到了真的會懷疑人生。推薦閱讀:
※為什麼以前上機課要戴鞋套?
※1500元如何配一台最新平台主機?
※如何通過程序估計cache大小?
※用天河二號打 CF 是一種怎樣的體驗?
※內存的頻率和容量哪個性能影響更大,更重要?