記一次定位偶發崩潰問題

本文記錄定位偶發崩潰問題的全過程,並給出相關建議,沉澱經驗。

背景描述

測試反饋,通過右鍵添加股票,有一定概率閃退,沒有dump以及更多的信息,崩潰頻率隨機。

問題復現

同測試進一步溝通,初步定位崩潰涉及的業務範圍,在本地Debug下,重試了若干次,發現同樣路徑的操作,在多次操作後,有那麼1、2次會觸發崩潰,Release下直接閃退,Debug下彈出“參數錯誤”提示框,並且沒有Dump信息。

定位問題

嘗試1:在Debug環境下,嘗試重現此崩潰,當觸發崩潰時,會有如下彈框。

image.png

嘗試通過彈框定位問題,發現此彈框是系統內置彈框,無法定位到彈框的構造函數,此路不通。

嘗試2:猜想,這類系統錯誤彈框肯定是框架層面來彈出的,至於爲什麼彈出,可能是因爲某些異常吧。嘗試在 wincore.cpp,查找異常處理來定位,在此處下斷點,當錯誤彈框再次出現時,斷點在系統異常處理處被觸發,具體如下圖所示:

image.png

到這裏,已經找到這個錯誤彈框是由誰彈出的,但此時的調用堆棧,對定位問題沒有幫助,因爲此時已進入異常處理流程,不是異常發生時的流程。此路不通。

嘗試3:重新梳理業務流程,走查代碼,沒發現什麼問題。只發現一處可優化點,當股票在當前分組已存在,後續就沒必要添加,這個優化點和本次崩潰沒關係,還要繼續查找。

嘗試4:繼續思考,同樣的操作流程,爲什麼有時候崩潰,有時候不崩潰呢?按理說,如果流程以及數據都正確,那麼無論重複多少次,最終都應該是一樣,但這裏卻有一定概率崩潰,很讓人費解!繼續查找代碼,沒發現什麼大問題,想得頭疼,於是下班,明天再看。

嘗試5:新的一天來了,整理下思緒,繼續前進。通過前面的分析,發現當崩潰出現時,往往是在點擊正常彈框之後,於是乎,懷疑是正常彈框的後續流程有問題,但在該後續流程的單步調試中,每個變量的值又都是正確的,沒發現問題,這就奇怪了。

嘗試6:既然在嘗試2中可以捕獲到異常,那麼可以從異常入手,在異常的構造函數下斷點,看看是哪裏觸發了該異常。這個方法果然有效,當再次崩潰時,直接就停在了異常的構造函數中,如下圖所示:

image.png

順着調用堆棧往上找,最終問題定位在 CMenuHelper::OnMenuSelect ,在正常彈框後,有一個引用變量,它的主體先釋放,而它在釋放後被繼續使用,造成崩潰,業界將這種內存問題稱之爲 use-after-free,簡稱爲 UAF

嘗試7:定位到問題後,想着既然前面的人寫出了這個隱含的問題,在當前文件其他地方,有沒有類似問題呢?小手一搜,果然發現了類似的問題,這說明,一個人,在同一個場景下犯下的錯誤,在不明確指出他錯誤時,他是很難知道自己做錯了。因此,如果後續遇到類似,基於之前自認爲是正確的做法,原樣拷貝過來,那麼在此會繼續犯錯,直到該問題被其他人發現

UAF問題復現

在弄清楚問題根因後,嘗試在本地構建最小復現UAFDemo,關鍵代碼如下:


	vector<CString> vTest(1, "test");
	CString& vTest1 = vTest[0];	
	CString strTag(vTest1);
	TRACE("1. strTag:%s \n", strTag);
	vTest.clear();			// 釋放
	strTag = vTest1;		// 釋放後繼續使用
	TRACE("2. strTag:%s \n", strTag);

在本地VS2015DebugRelease環境中嘗試了很多次,確實會觸發崩潰,但不是每次都觸發崩潰,而是有一定概率,具體爲什麼,暫未深究。如果釋放點和後續使用點相距較遠,甚至是傳遞到其他函數中,那麼這類問題將更加難以查找和定位。

即使在Debug環境中觸發,直接從崩潰堆棧上看,得不到定位問題的有價值信息,後續很難跟進。

從正確性上看,單步執行沒問題,但當使用者多了後,這種隱藏的Bug就會時不時冒出來,復現路徑難以捉摸,沒有Dump,定位難度非常大。

小結

此問題是對已釋放的引用變量進行操作,導致異常內存訪問,從而崩潰。

因此,在後續工作中,針對引用類型變量,要非常注意它的生命週期,不要在它的本體釋放後繼續使用。

針對 use-after-free 這類內存偶發崩潰問題,查找定位難度很大,可有如下方法:

  1. 根據測試反饋的場景,儘可能縮小業務邏輯範圍
  2. 按照測試提供的步驟,嘗試各種方式和路徑的操作,探索總結崩潰發生的大致規律和路徑
  3. 如有異常提示,可從該異常入手,在異常基類的構造函數中下斷點,再反覆嘗試,直到問題重現
  4. 當斷點觸發後,往上回溯堆棧,定位問題根因
  5. 查找同類型場景,是否存在相同問題

經驗小結:

  • 拷貝粘貼的操作會複製那些你以爲是對的,但實際上是錯誤的實現,這類錯誤會不斷積累,直到被用戶或測試發現。
  • 當一頭扎進去一個難題很深很久,想了很久沒有頭緒時,不妨出去走走或者聽聽歌曲,讓大腦放輕鬆,過段時間再看這個問題,說不定會有新的思路。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章