debug tips

最近在協助debug一個crash的問題,紀錄一下debug的思路。

它的現象是當發soap https request時,在ssl handshake階段就出問題(出現有問題的封包內容或是程式crash)

crash問題比較麻煩的是有時候發生,有時候正常,但一般情況,如果能夠經常重現,比較能夠快一點界定問題。常見的C/C++程式crash出現是在multithread lock問題導致資料的內部結構不一致或毀損。

crash在openssl內部,觀察comp_methods的值,發現內容怪怪的

ssl compression相關的資訊是在初始化就會做完的,第一個合理的猜測:在初始化的時候,可能有多thread同時做ssl init,導致global object的內部結構race condition,所以先在SSL_library_init裡面插printf看看。

這個版本使用的openssl是1.0.2,參考 https://www.openssl.org/docs/man1.0.2/man3/SSL_library_init.html

發現有多個thread呼叫此init函式多次,理論上init只需要一次就好,因此先確定是哪些地方直接或間接呼叫到。這邊用thread來找,當然在環境許可下,也可以直接設breakpoint看callstack

透過callstack先找到解決呼叫多次的問題,

發現ssl init多次是有個lib內部多次呼叫curl_global_init (當然也多次呼叫了curl_global_cleanup) https://curl.haxx.se/libcurl/c/curl_global_init.html

但事實上SSL_library_init被設計成可以呼叫多次(只是不能reentrant,或是thread同時呼叫),參考: https://libwebsockets.org/pipermail/libwebsockets/2016-May/002366.html

所以以上的狀況看起來不是crash問題的原因

SSL_library_init的開始和結尾地方加上printf,觀察thread id,發現其實 SSL_library_init 執行沒有overlap,也就是造成內部結構的問題不在這

前面提到有lib多次呼叫curl global cleanup看起來很可疑,curl_global_cleanup應該只被呼叫一次,而且是在程式最後結束時。因此第二個合理的猜測,應該是在cleanup處將openssl內部global object destroy。

easy.c
vtls/vtls.c
vtls/openssl.c
vtls/openssl.c (openssl 1.0.2: OPENSSL_VERSION_NUMBER 0x100020efL)

果然看到在curl global cleanup裡面呼叫到了SSL_COMP_free_compression_methods,因此前面提到有個lib多次呼叫 curl_global_cleanup ,導致其他地方要使openssl function時因為compression methods內容有問題而crash。

所以這個問題就單純是其中某個lib呼叫的流程時機錯誤,將curl global init/cleanup移到main的一開始和結束就可以了

This entry was posted in Tips. Bookmark the permalink.

Leave a Reply