contributed by < flawless0714
>
Kernel 版本: 4.15.0-47-generic
原先擔心 kernel module 中的這項參數會影響測試,理解後得知其是在 accept()
成功前 queue 中最大能存放的連線要求,然而我的測試程式是在 accept()
(以測試程式的角度看是 connect()
) 成功後才會開始計時,所以這邊除非 queue 太容易塞滿,導致測試程式的部份執行緒沒有被涵蓋到 kecho 並行處理通訊時的情境。
工作於 kernel-space 的 thread。
建立新 kthread 並開始執行,回傳值為 task_struct
。
通知 kthread 準備關閉(將 kernel_should_stop
旗標設為 true),並等待直到 threadfn
結束(由此可知 threadfn
中需要有一直驗證停止旗標的實做)。其中會把 kthread->kthread_should_stop
設為 true。此函數也用於喚醒 kthread。
當 kthread 閒置時需要將其 block,以免發生浪費資源的情況。常見的 block 方法為使用 schedule()
、ssleep()
。
schedule()
睡眠之運作方式首先使用 set_current_state()
將 kthread 的 state 改成 TASK_INTERRUPTIBLE
,以使得待會可以被移出 run queue (排程器排程時會從 run queue 挑 task),接著呼叫 schedule()
,告知排程器可以切到其他 task 了,也就是讓出 CPU 資源,至此這個 kthread 即開始睡眠。假如要喚醒這個 kthread,我們可以使用 wake_up_process()
(帶入參數為目標 kthread 的 task_struct*
),這個函數會把 kthread 的 state 切回 TASK_RUNNING
,並將其它放回 run queue,接著就等待排程器下一次排程了。
create
, bind
與 listen
等 socket 設定。listen
後,建立第一個 kthread,這個執行緒為 server daemon,其負責進行 listen
,每當有新進連線時,它會開新的執行緒(server worker)來 handle 新連線。get_response
來等待 client 端傳入訊息。send_request
以回傳 client 傳入之訊息,以達到 echo 訊息的功能。rmmod
)。由不斷 evaluate
kthread_should_stop
回傳的布林值去判斷是否需要關閉。
pthread_attr_getdetachstate
確認過) 的 pthread 可以在 pthread_join
結束後(檢查過 retval 正常)才結束?當輸入資料量大到一定量時,會回傳亂碼,例如 /
下的檔案名稱,還有出現過自己的 kernel cmdline…。
多次測試後發現就算輸入資料很小也會發生回傳檔名,而且是發生在
get_response
的時候,所以可以說是telnet
自己傳過去的,或是kmalloc
配置的記憶體沒有初始化的關係(首次 echo 即發生此問題),待測試。看來是
kmalloc
配置的記憶體沒初始化造成的,剛剛測試配置完馬上用memset
初始化記憶體,就沒再遇到這問題了,不然每次telnet
第一次連線就算你直接按 enter 鍵送空字串,kecho
也會回傳一串亂碼。不知道這算不算漏洞…,連 cmdline (/proc/cmdline) 都露出來了,這樣可能整個/
下的檔名都可用這招掏出來…。想想覺得這應該不算漏洞,因為這個 module 算是沒有經過驗證之類的檢查,而且載入後 kernel 就變成 tainted kernel 了。
修復:
如上述,只需於使用 buf
前初始化記憶體即可。
echo_server_worker
:
msg buffer 沒有重置,進而導致每筆新訊息會與前一筆訊息重疊(前提是當前訊息長度小於前一次,否則新訊息會完全覆蓋舊訊息)。
修復:
透過在每次 echo 結束後重置 buffer,須注意由於 kthread 在關閉前還會執行一套接收(get/send _request),而由於我們有做清空的動作,所以進行最後一次 send_request
時帶入的 size
會是0,這會讓 send_request
中的 kernel_sendmsg
帶入的 size
參數拿到18446744073709551615(以 size_t 來計算 0 - 1),進而造成 kernel_sendmsg
執行時發生 kernel panic (null dereference at 000000000000)。話說這是個有趣的故事的開始,遇到 panic 後我想說這個 size 對記憶體位置來說已經是天文數字了,於是就試試小一點的,我直接把 size
給定 BUF_SIZE + 1000
,想看看 panic log 會說我想存取哪段記憶體,答案是 null dereference at 0000000013E8
,看到這邊還稍有意義(13E8
轉為十進制是5096),再接著我又好奇假如放248(x86_64 最大定址位置)會發生啥事,結果還沒測到248-1,只是先測248就差點把我的 /
搞爆了,會這麼說是因為我開不了機…,只能用 ctrl + fx
進 TTY 而開不了視窗桌面,而且原本的密碼不能登入..,然後進 TTY 後還跳出 kdump 無法初始化的錯誤,並且提示 /
已無可用空間..,結果 df -h
一看發現 /
被用光了(原本還有 4GB 左右),實在不懂這是怎麼造成的,等虛擬機有成功打開 kdump 再來測試看看248-1會發生什麼事,不然怕下次我的實體機沒這麼好運還可以開機…。
echo_server_worker()
:
send_request()
:
測試用之 client 端程式碼。
gettimeofday
)數據出現負號。發生原因為平均數量級差異過大造成溢位,故須捨棄 ns 級的時間量測 API,改用更粗糙的單位。while(flag);
去等待其他 thread 的建立(等1000個 thread 的實驗已經等了至少20分鐘了…,最後單次傳輸超過一秒被強制停止。推測發生原因是這支 process 的資源被那些已建立的 thread 拿去卡在 while
,所以後續建立的 thread 其實沒什麼資源可以用)。
修好後不只問題解決,連 concurrent thread 有數百個時 CPU 也不會有太大的負載(原本是全部核心使用率都接近100%)。
pthread_join
後使用 usleep
稍待一會再開始下一輪(concurrent thraed 數量相同,此舉之目的為取平均時間)測試,則 thread 用來索引量測時間陣列的變數(idx
)會發生多次 idx++
的問題,進而導致 OOB access of array。目前已經確認(手動使用 pthread_attr_init
去初始化 attr,再使用 pthread_attr_getdetachstate
去檢驗)過 pthread_join
使用的預設 attr
中的 detachstate
是 JOINABLE
的(雖然 manpage 已經有說這是預設了,但我快找不到問題點了QQ,所以還是檢查一下)
剛剛測試又發現新情境,在沒有使用
usleep
的情況下若從初始數量大一點(e.g. 500)的 concurrent thread 開始測試即不會發生上述的 OOB 問題,問題越來越深了..。找到問題了,是我太蠢…,我把
cur_thread_cnt
的初始值設0,這造成pthread_join
的迴圈在cur_thread_cnt
為 0 (其實是單個 thraed) 時連進都進不了,因為我的迴圈設定是for (int x = 0; x < cur_thread_cnt; x++)
。
完全不可考…,晚點更新修正版本。(此版本在 thread function 中使用 while 迴圈來等待一個 global 的旗標,此舉耗費非常多資源在單個 thread 上面)
[修正版] (添加 pthread_cond_wait
以節省運算資源之浪費)
由圖中可發現通訊時間平均花費了 10ms 左右
telnet
時建議使用 close
or q
command 關閉連線,若用 ctrl
+ z
,會讓未關閉的連線一睡不起 (suspend),進而造成下一次 insmod
時發生位置佔用的問題(address already in use),目前解法為重開機。可用下方 command 來查看哪些 port 正在使用:
linux2019