[經驗教訓總結]協議包頭結構體定義不嚴謹造成的錯誤
阿新 • • 發佈:2018-12-27
今天遇到一個問題,除錯了一天.
大致描述一下,移植一個開源專案的程式碼,原來在mips平臺上執行正常,後來到arm平臺的機器上執行,結果執行時出錯了.
一般的,這樣的問題,腦子裡面第一下的反應就是由於位元組序問題造成的.
我起初也是這麼想的.因為從出錯的現象來看,是某個欄位不符合要求造成的出錯.於是沿著這個思路去找BUG.抓包來分析,看上去也是這樣的.但是,追蹤的過程中發現,有多次收發報文的過程,這個欄位,或者說擁有這個欄位的結構體在多處都有使用,改了一處,在別的地方其它欄位又有報錯.
回頭看程式碼,發現在最開始解析包頭的時候,已經造成了緊跟著包頭的某個欄位出現異常,於是,想到是不是在不同的平臺上,sizeof(某結構體)的數值不一樣造成,要驗證這一點,給包頭結構體的定義加上嚴格按照一個位元組對齊的限制,重新執行程式,可以了.
最後再來稍微詳細一些看這個問題,假設包頭結構體的定義是:
typedef unsigned short u16;
struct header
{
u16 a;
u16 b;
u16 c;
}; 如果解析的時候,sizeof(struct header) != sizeof(u16) * 3,那麼使用sizeof(struct header)解析接收到緩衝區的資料就會出現問題,因為它會對緊跟著的資料也造成影響.程式的異常正是源於此.在程式碼的處理中,首先接收包頭,對包頭的資料進行了位元組序轉換,然後,又對緊挨著包頭的結構體進行了相同的位元組序轉換,由於包頭結構體的位元組序轉換同時影響了緊挨著的結構體中的資料,所以這些資料實際上被進行了兩次的位元組序轉換,這才造成了這個問題"看上去"是位元組序轉換不當造成的"表面原因",如果跟著這個原因繼續跟蹤下去,以這個思路解決問題,治標而不治本.
總結:
1. 收發資料的結構體定義需要嚴謹一些,如果不能確定如何對齊,最好自己定義一個對齊的標準.
2. 經驗有的時候也不見得就是好事,有時候會讓自己陷入思維定式的怪圈,比如在這個問題的處理上,由於問題在切換了硬體平臺的時候才出現,正好又是兩個位元組序不一樣的硬體平臺,所以經驗將我的思路導向了位元組序不正確這個方向上.
大致描述一下,移植一個開源專案的程式碼,原來在mips平臺上執行正常,後來到arm平臺的機器上執行,結果執行時出錯了.
一般的,這樣的問題,腦子裡面第一下的反應就是由於位元組序問題造成的.
我起初也是這麼想的.因為從出錯的現象來看,是某個欄位不符合要求造成的出錯.於是沿著這個思路去找BUG.抓包來分析,看上去也是這樣的.但是,追蹤的過程中發現,有多次收發報文的過程,這個欄位,或者說擁有這個欄位的結構體在多處都有使用,改了一處,在別的地方其它欄位又有報錯.
回頭看程式碼,發現在最開始解析包頭的時候,已經造成了緊跟著包頭的某個欄位出現異常,於是,想到是不是在不同的平臺上,sizeof(某結構體)的數值不一樣造成,要驗證這一點,給包頭結構體的定義加上嚴格按照一個位元組對齊的限制,重新執行程式,可以了.
最後再來稍微詳細一些看這個問題,假設包頭結構體的定義是:
typedef unsigned
struct header
{
u16 a;
u16 b;
u16 c;
}; 如果解析的時候,sizeof(struct header) != sizeof(u16) * 3,那麼使用sizeof(struct header)解析接收到緩衝區的資料就會出現問題,因為它會對緊跟著的資料也造成影響.程式的異常正是源於此.在程式碼的處理中,首先接收包頭,對包頭的資料進行了位元組序轉換,然後,又對緊挨著包頭的結構體進行了相同的位元組序轉換,由於包頭結構體的位元組序轉換同時影響了緊挨著的結構體中的資料,所以這些資料實際上被進行了兩次的位元組序轉換,這才造成了這個問題"看上去"是位元組序轉換不當造成的"表面原因",如果跟著這個原因繼續跟蹤下去,以這個思路解決問題,治標而不治本.
總結:
1. 收發資料的結構體定義需要嚴謹一些,如果不能確定如何對齊,最好自己定義一個對齊的標準.
2. 經驗有的時候也不見得就是好事,有時候會讓自己陷入思維定式的怪圈,比如在這個問題的處理上,由於問題在切換了硬體平臺的時候才出現,正好又是兩個位元組序不一樣的硬體平臺,所以經驗將我的思路導向了位元組序不正確這個方向上.