目錄
1、問題說明
2、導出dump文件時只是遇到了INT 3軟中斷,并沒有發生異常崩潰
3、函數中發生了棧內存越界,導致線程的棧回溯出異常,堆棧中只顯示一行函數調用記錄
3.1、處理Json數據時產生了異常
3.2、函數中發生棧內存越界,可能會導致線程的棧回溯出異常
3.3、將打印出來的Json數據與代碼對照起來看,發現是數組越界了(棧內存越界)
4、遇到INT 3中斷,可能此時代碼出現了問題,可以查看此時的函數調用堆棧(附上INT 3軟中斷的詳細說明)
5、最后
C++軟件異常排查從入門到精通系列教程(核心精品專欄,訂閱量已達8000多個,歡迎訂閱,持續更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++實戰專欄(重點專欄,專欄文章已更新500多篇,訂閱量已達6000多個,歡迎訂閱,持續更新中...)
https://blog.csdn.net/chenlycly/article/details/140824370C++ 軟件開發從入門到實戰(重點專欄,專欄文章已更新300多篇,歡迎訂閱,持續更新中...)
https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能開發匯總(專欄文章列表,歡迎訂閱,持續更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++軟件分析工具從入門到精通案例集錦(專欄文章,持續更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795開源組件及數據庫技術(專欄文章,持續更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html網絡編程與網絡問題分享(專欄文章,持續更新中...)
https://blog.csdn.net/chenlycly/category_2276111.html? ? ? ?近日項目中遇到了一個比較有代表性的軟件異常案例,其中有兩點值得關注,一是在崩潰前先產生了INT 3軟中斷(系統模塊中人為添加了INT 3軟中斷),二是代碼中發生了棧內存越界導致線程棧回溯出異常。本文來詳細講述一下該案例的排查過程以及涉及到的這兩個要點,以供大家借鑒或參考。
1、問題說明
? ? ? ?測試同事那邊反饋了一個崩潰,但軟件中安裝的異常捕獲模塊并沒有感知到,沒有生成dump文件。這個問題在當前場景下是必現的,于是測試同事直接將Windbg附加到目標進程上進行調試,然后復現問題,嘗試通過動態調試的Windbg去感知并捕獲異常。
? ? ? ?問題復現后,Windbg中斷下來,測試同事輸入kn命令將此時的函數調用堆棧打印出來,然后將堆棧截圖發到討論群中:
然后用.dump命令從當前正在調試的Windbg中導出了dump文件,將dump文件發給我。
? ? ? ?取到dump文件后,用Windbg打開,輸入.ecxr切換到異常上下文,然后輸入kn,打印出來的堆棧只顯示一行,如下所示:
這行中調用的是系統庫接口(ntdll庫中的RtlCaptureStackContext接口),并沒有看到我們軟件業務模塊的接口調用,這點很奇怪。
2、導出dump文件時只是遇到了INT 3軟中斷,并沒有發生異常崩潰
? ? ? ? 后來看到討論群里的截圖,截圖中可以看到當時只是產生了一個INT 3中斷:
并不是崩潰,這個INT 3會讓正在調試的Windbg中斷下來,測試同事以為Windbg中斷下來就是發生崩潰了(測試同事只會簡單地使用Windbg,對于一些細節不了解),于是導出了dump文件。所以后面我們拿到dump文件后,使用.ecxr和kn命令并沒看到有效的函數調用堆棧,只能看到系統庫接口的調用。
? ? ? ?因為這個問題是必現的,我這邊想直接用Windbg抓一次,抓一次發生異常崩潰時的dump。我先將軟件運行起來,然后將Windbg附加到軟件進程上,讓測試同事把問題復現出來。問題復現時,先產生了一個INT 3中斷:
輸入g命令將這個中斷跳過去,然后就發生了Access Violation內存訪問違例的異常(此時才發生異常崩潰),于是用.dump命令導出了dump文件。
因為Windbg動態調試時非常卡,根本沒法輸入命令,使用事先拷貝好的.dump命令導出了dump文件。
? ? ? ?關于如何使用Windbg靜態分析dump文件、如何使用Windbg動態調試目標進程、何時使用Windbg靜態分析及何時動態調試以及使用Windbg分析問題的諸多細節與技巧,我在此就不贅述了,可以查看我之前寫的專題文章:
使用Windbg分析dump文件定位軟件異常的方法與操作步驟https://blog.csdn.net/chenlycly/article/details/146005441使用Windbg調試目標進程排查C++軟件異常的一般步驟與要點分享
https://blog.csdn.net/chenlycly/article/details/145826705何時使用Windbg靜態分析?何時使用Windbg動態調試?
https://blog.csdn.net/chenlycly/article/details/131806819【C++軟件調試技術】使用Windbg分析軟件異常時的諸多細節與技巧總結
https://blog.csdn.net/chenlycly/article/details/140742933
3、函數中發生了棧內存越界,導致線程的棧回溯出異常,堆棧中只顯示一行函數調用記錄
3.1、處理Json數據時產生了異常
? ? ? ?接下來用Windbg打開dump文件進行分析,輸入.ecxr切換到發生異常的上下文,輸入kn命令,結果堆棧中也只顯示一行,不過這行中看到了業務模塊接口的調用:
于是找到堆棧中模塊的pdb文件,設置到Windbg中,重新將函數調用堆棧打印出來,可以看到函數中的具體行號,提供給該模塊的維護人員,讓他們詳細分析一下崩潰的原因。
? ? ? 點開這行前面的索引鏈接,顯示出當前函數中相關變量的值,看到傳入該函數的是Json數據,函數中要把這個Json數據要按節點讀取出來,保存結構體變量中,然后投遞給上層。看到Json數據,猜測可能是處理Json數據時出了異常,可能是以下原因導致的:
1)在解析Json數據時產生了異常,比如平臺側修改了Json數據的格式,沒通知終端側,導致終端側解析Json數據的代碼產生了異常,這樣的問題我們以前遇到過。
2)可能是Json串中某個節點的值是不正常的異常值,導致代碼處理邏輯出問題。比如是個異常大的值,如果用該值作為for循環的條件,則會導致死循環。
3)Json數據中包含的數據對象個數大于本地對應的數組的個數,而本地沒有判斷數組是否超過長度,從而導致操作數組越界。本案例中的問題,就是這個原因導致的。下面會詳細講解本案例的這個問題場景。
3.2、函數中發生棧內存越界,可能會導致線程的棧回溯出異常
? ? ? ?此處堆棧中只顯示一行,有些奇怪,難道是當前函數中發生了棧內存越界,直接把堆棧中保存的主調函數的ebp棧基址覆蓋了(通過函數中的棧基址將函數調用堆棧回溯出來),導致線程棧回溯不出來了?經后面分析,確實是這個原因導致的。
因為棧是從大地址向小地址方向分配使用的,如果當前函數中發生在棧內存越界,是從操作的起始地址向后面的大地址方向越界,可能會越界到存放主調函數的ebp棧基址值的內存區域(篡改了棧基址值),這樣會導致回溯函數調用堆棧時出現問題,所以上面只能看到當前函數的調用, 且只顯示一行調用記錄:
? ? ? ? 關于函數調用的棧分布以及線程函數調用堆棧的棧回溯原理,可以查看我之前寫的專題文章:
C++函數調用棧分布詳解(理解多個問題的切入點)https://blog.csdn.net/chenlycly/article/details/121001096C++棧回溯原理(C++異常排查面試題)
https://blog.csdn.net/chenlycly/article/details/121002139
??? ? ? ?在這里,給大家重點推薦一下我的幾個熱門暢銷專欄,歡迎訂閱:(博客主頁還有其他專欄,可以去查看)
專欄1:【C++軟件異常與異常排查從入門到精通系列教程】(該精品技術專欄的訂閱量已達到10000多個,專欄中包含大量項目實戰分析案例,有很強的實戰參考價值,廣受好評!專欄文章持續更新中,已經更新到200篇以上!歡迎訂閱!)
C++軟件調試與異常排查從入門到精通系列文章匯總https://blog.csdn.net/chenlycly/article/details/125529931
本專欄根據多年C++軟件異常排查的項目實踐,系統地總結了引發C++軟件異常的常見原因以及排查C++軟件異常的常用思路與方法,詳細講述了C++軟件的調試方法與手段,詳細介紹分析C++軟件問題的常用分析工具,以圖文并茂的方式給出具體的項目問題實戰分析實例(詳細講述分析排查過程,很有實戰參考價值),帶領大家逐步掌握C++軟件調試與異常排查的相關技術,適合基礎進階和想做技術提升的相關C++開發人員!
考察一個開發人員的水平,一是看其編碼及設計能力,二是要看其軟件調試能力!所以軟件調試能力(排查軟件異常的能力)很重要,必須重視起來!能解決一般人解決不了的問題,既能提升個人能力及價值,也能體現對團隊及公司的貢獻!
專欄中的文章都是通過項目實戰總結出來的,包含大量項目問題實戰分析案例,有很強的實戰參考價值!專欄文章還在持續更新中,預計文章篇數能更新到200篇以上!
專欄2:【C/C++實戰進階】(該專欄涵蓋了C++多方面的內容,是當前重點打造的專欄,訂閱量已達8000多個,專欄文章已經更新到500多篇,持續更新中...)
C/C++實戰進階(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的開發實戰為基礎,總結并講解一些的C/C++基礎與項目實戰進階內容,以圖文并茂的方式對相關知識點進行詳細地展開與闡述!專欄涉及了C/C++領域多個方面的內容,包括C++基礎及編程要點(模版泛型編程、STL容器及算法函數的使用等)、數據結構與算法、C++11及以上新特性(開源代碼中可能會用到很多新特性(比如WebRTC開源庫),日常編碼中也會用到部分新特性,面試時也會頻繁地涉及到,學習新特性很有必要)、常用C++開源庫的介紹與使用(比如SQLite、libcurl、libwebsockets、libevent、jsoncpp/RapidJson、Redis、RabbitMQ、MongoDB、MQTT、ZooKeeper、OpenCV、FFmpeg、SDL、GStreamer、Live555、ReactOS等)、代碼分享(調用系統API、使用開源庫)、常用編程技術(動態庫、多線程、多進程、數據庫及網絡編程等)、軟件UI編程(Win32/duilib/QT/MFC)、C++軟件調試技術(引發C++軟件異常的常見原因分析與總結、排查C++軟件異常的手段與方法、分析C++軟件異常的基礎知識、使用常用軟件分析工具分析C++軟件問題、多個項目實戰問題分析案例分享等)、設計模式(單例模式、工廠模式、觀察者模式、狀態模式等)、網絡基礎知識與網絡問題分析進階內容(實戰問題分析實例分享)等。本專欄的內容都是建立在項目實踐的基礎上,來源于項目實戰,服務于項目實戰,很有實戰參考價值!
專欄3:【分析C++軟件問題的實用軟件與高效工具實戰案例集錦】
C++常用軟件分析工具從入門到精通案例集錦匯總(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++軟件輔助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro以及內存泄漏檢測工具等,本專欄詳細介紹如何使用這些工具去巧妙地分析和解決日常工作中遇到的問題,很有實戰參考價值!
專欄4:【VC++常用功能代碼封裝】
VC++常用功能開發匯總(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
將10多年C++開發實踐中常用的功能,以高質量的代碼展現出來。這些常用的高質量規范代碼,可以直接拿到項目中使用,能有效地解決軟件開發過程中遇到的問題。
專欄5:【C/C++軟件開發從入門到實戰】(本專欄涵蓋了C++多方面的內容,是當前重點打造的專欄,專欄文章已經更新到300多篇,持續更新中!歡迎訂閱!)?
C++ 軟件開發從入門到精通(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根據多年C++軟件開發實踐,詳細地總結了C/C++軟件開發相關技術實現細節,分享了大量的實戰案例,很有實戰參考價值。
3.3、將打印出來的Json數據與代碼對照起來看,發現是數組越界了(棧內存越界)
? ? ? ?查看運行日志看到平臺傳過來的Json數據中某類對象有26個:
但本端存放這些對象的數組中只有10個元素(數組長度是10),如下所示:
代碼中在將Json串中某對象數據保存到數組中時,沒有判斷數組的長度,直接將26個對象保存進來了,所以導致操作數組時越界了。該數組所在的結構體定義成局部變量,所以發生的內存越界應該是棧內存越界,然后將堆棧中存放的主調函數的ebp棧基址給覆蓋了,導致棧回溯出現了問題,所以堆棧中只看到一行記錄。這樣引發問題的原因和現象就完全一致了,能對得上了!
4、遇到INT 3中斷,可能此時代碼出現了問題,可以查看此時的函數調用堆棧(附上INT 3軟中斷的詳細說明)
? ? ? ?如果要查看完整的函數調用堆棧,可能產生INT 3中斷時可以看到,查看此時的函數調用堆棧可能也能找到問題。
? ? ? 在代碼的異常分支中可以插入軟中斷的匯編代碼_asm INT 3:
// manual breakpoint
_asm INT 3;
printt("HeLlo INT 3!\n");
當代碼執行到該異常分支時就會產生INT 3軟中斷,所以遇到INT 3中斷可能就是為了提示當前代碼可能出問題了。
? ? ? ?關于INT 3軟中斷的詳細說明,翻看了張銀奎老師的《軟件調試》一書部分章節:(這本書推薦大家看一下,要多看書、看好書!)
? ? ? ?可能是在代碼的問題分支中添加了_asm INT 3這條語句,當代碼執行到該異常分支時就會產生INT 3軟中斷,讓調試器中斷下來,提示用戶當前程序可能出了異常,中斷下來好讓用戶查看此時的函數調用堆棧,然后進行分析。
5、最后
? ? ? ?軟件中發生棧內存越界,將主調函數的ebp棧基址給改寫了,導致函數調用堆棧回溯出問題,以及代碼中人為加入INT 3軟中斷,之前很少遇到,這次算是見識到實戰案例了,所以在此記錄總結一下,分享給大家。