撰稿 | 言征
Ariel Miculas,是一位開源貢獻者,目前在思科任職軟件工程師,最近他在自己的博客上開噴Linux內核:“為什么我貢獻了問題和補丁代碼,最后貢獻者的名單里卻沒有我?”
1、自封的Linux內核“貢獻者”
翻開Ariel的博客,他這樣介紹自己:“我是一位充滿激情的軟件工程師,擁有網絡安全碩士學位。我感興趣的領域是系統編程,包括管理程序、操作系統,以及最近的Linux文件系統。我也是一個開源貢獻者,以下是我貢獻的一些項目:Linux內核、capnproto-rust、squashfuse。”
可以看出,Ariel認為自己是對Linux內核有貢獻的。然而讓他氣憤地是,他的第一個內核貢獻卻被內核維護者被無情剝奪了。
2、復現了六年前的Linux內核Bug,一直無解
GDB是Linux下的調試利器,而gdbserver是配合gdb實現遠程調試的工具。大約在一年半前,Ariel致力于解決掉一個有關gdbserver遠程項目調試的問題:gdbserver 無法調試在 PowerPC32 架構上運行的多線程應用程序。與 gdbserver 的連接已斷開,并且無法再控制調試會話。慶幸的是,很多人已經調查過這個問題,Ariel團隊仍然不確定問題出在哪個軟件組件上:它可能是工具鏈、gdbserver、Linux 內核或他們應用的自定義補丁內核樹的頂層。一時間難以找到根本原因。
Ariel結合現有分析和谷歌搜索,對這個問題進行了深入研究,終于取得了第一個突破:他找到了一個與其描述問題癥狀相同的電子郵件線程,而且還指出了引入它的一個關于Linux內核的確切提交(kernel/git/torvalds/linux.git)。
圖片
引入該錯誤的補丁將thread_struct thread的定義從task_struct的中間移動到了末尾,這個更改看起來貌似無害,但會帶來一些低級問題——
我看到的是 gdbserver 為每個線程發送 SIGSTOP 到內核并等待響應。內核確實接收到所有信號,但僅在錯誤情況下響應其中的一些信號。
然后,它與我的“ps”輸出相匹配,因為我看到某些線程未處于 pthread_stop 狀態,然后 gdbserver 被掛起。
圖片
問題在于,在與 gdbserver 交互后,某些線程處于錯誤的進程狀態,并且 gdbserver 無法再控制它們。
3、古老的問題往往源于簡單的錯誤
Ariel 花了 3-4 天閱讀 PowerPC 架構相關的提交描述以及task_struct的版本變化,卻發現這個問題并沒有在后續的內核版本得到解決。
確定問題何時復現之后,Arielkaishi使用一款工具來檢查 task_struct的布局,同時用 ftrace來確定調試進程的線程何時被調度,最后終于找到了原因:可能是內存損壞的問題:與其他線程不同,卡住的線程僅被調度一次。然而,一開始其實他就否認了這個問題,因為在Linux郵件列表里有關原始線程的描述:
緩沖區的內容始終為零并且不會改變。所以至少沒有人向緩沖區寫入非零值。
后來,Ariel研究了如何在 Linux 上使用硬件斷點,最終基于某個 stackoverflow 的答案實現了一個新的 Linux 內核模塊,該模塊可以在__state 字段上放置一個硬件斷點 ,以找出到底是誰寫入它。
圖片
https://elixir.bootlin.com/linux/v6.5.5/source/include/linux/sched.h#L746
Ariel興奮地總結了找到這個Bug的方法:通過自定義內核模塊顯示了寫入__state字段的位置的堆棧跟蹤。task_struct一個異常值揭示了 ptrace_put_fpr中的緩沖區溢出。
這導致重要字段被 task_struct覆蓋,例如__state存儲進程狀態的字段,內核還使用它來跟蹤調試器停止了哪些進程等等。
溢出的原因也很簡單:內核需要對 64 位元素數組進行索引,但 fp_state.fpr 數組中只有 32 個。
4、向上游發送補丁,卻被維護者擺了一道
發現解決問題的過程非常極客,但發送補丁開始之后,卻讓Ariel感覺不爽了。
Ariel后來向 Linux 內核安全團隊 (security@kernel.org) 提交了第一個補丁,不幸的是,由于這個郵件列表是私人的,所以無法鏈接到原始補丁。
后來PowerPC 維護者Michael Ellerman跟進并告知,他將私下聯系來解決這個問題。實際上,Ariel已經向他發送了兩個修復該問題的補丁:發送到安全郵件列表的原始補丁和另一個版本 (與第一個完全不同),第二個版本解決了在回復最初提交的內容時收到的一些建議。
Michael Ellerman 還是沒有接受這些建議,而是實施了他自己的修復版本。Ariel有些沮喪,表示:希望能接受自己的補丁,這樣就可以獲得修復此問題的榮譽并成為內核貢獻者。同時也愿意與維護者合作,解決他的反饋并發送補丁的后續版本。
然而維護者的答復卻讓Ariel感到非常困惑和侮辱:
抱歉,我想以不同的方式修復它。如果您想成為 Linux 內核貢獻者,這里有一個您可以解決的問題。
“他不想因為解決問題而獲得認可,而是想讓我做更多的工作。我和我的公司應該因解決這個問題而獲得應有的榮譽,特別是考慮到我們為此付出了多少努力。”
5、侮辱性極強:貢獻了補丁,卻只被授予了“報告者”的頭銜
Ariel認為只獲得“報告者”標簽非常不公平。因為“報告者”報簽的分量遠不及貢獻者標簽——它是向那些發現錯誤并報告錯誤的人表示感謝,并希望能夠激勵他們將來再次幫助我們。
事后,Ariel對內核社區的印象急轉直下。相反,他因自己的工作沒有得到適當的認可而感到被貶低和憤怒。
“我花了很多時間和精力進行根本原因分析,修復錯誤,測試和驗證修復,從公司其他工程師那里獲取反饋,使修復適應最新的內核版本,并向 PowerPC 維護者 Michael Ellerman 發送兩個不同的補丁。他沒有接受我的補丁或指導我找到更好的解決方案,而是繼續實施自己的修復方案,只對我報告問題給予認可(而且這個問題還是六年前已經報告過)。”
6、Linux內核維護,對于“貢獻者”有些吝嗇
此事爭議的一個焦點在于,如果維護者已經閱讀了Areil的補丁,之后改變了一些風格,并自己提交這個補丁,那么就會存在借用補丁提交者的事實。
又或者即便提交者的代碼很糟糕,但也不應該很不屑的回復一句:我想用不同的方式修復它。畢竟,如果沒有沒有原始代碼,我們連重構修復的機會都沒有。
誠然,出于質量目的,維護者可以堅持自己的引進內核的代碼,但很顯然,Ariel是該補丁的共同貢獻者,而不僅僅是Bug的“報告者”。
通過Reddit上用戶的評論也能看出,Linux內核維護者對于提交補丁代碼者的認可力度不足已經不是個例:
“前幾次我向 Linux 內核提交建議補丁(在通過 LKML 半自動提交成為可能之前),我與維護者(本例中為 Ted Tso)進行了對話。一旦他對我的工作的正確性感到滿意,他就合并了補丁,一切都很好。我從未要求過,也沒有得到過任何榮譽。”
希望這樣的情況能夠得到改善,否則會讓一些開源貢獻者們失去對“開源”的熱愛。
參考鏈接:https://ariel-miculas.github.io/How-I-got-robbed-of-my-first-kernel-contribution/