# 2021q3 Homework1 (quiz1) contributed by < asahsieh > [題目連結](https://hackmd.io/@sysprog/linux2021-summer-quiz1) ## 電腦環境 - Hardware: - Processor 2.7 GHz Dual-Core Intel Core i5 - Memory 16GB 1867 MHz DDR3 - Host OS: - macOS Catalina Version 10.15.2 - VM: - Canonical Multipass - OS: Ubuntu with kernel in `5.4.0-65-generic` - Switch to [lima](https://github.com/lima-vm/lima) on 09/05/2021 ## Compile code - `$ Make all` - get error: ```shell Makefile:9: *** missing separator (did you mean TAB instead of 8 spaces?). Stop. ``` - sol.: - 因為Makefile中的法則,行首必須為`<tab>`,不能為空白[^first] - 而我的`~/.vimrc`裡有將tabs置換成空白鍵;導致此錯誤: ```shell \" Use spaces instead of tabs set expandtab ``` - 在`vim`裡將法則前的空白字元置換成tab;即可正確執行`make`: `:%s/^[ ]\+/\t/g` ## 題目解析 ### 用法 - `$ insmode`的作用,擷取部分manual: *NAME* insmod - Simple program to insert a **module** into the Linux Kernel *DESCRIPTION* insmod is a trivial program to insert a module into the kernel. Most users will want to use modprobe(8) instead, which is more clever and can handle module dependencies. Only the most general of error messages are reported: as the work of trying to link the module is now done inside the kernel, the **dmesg** usually gives more information about errors. - 執行`$ sudo rmmod hideproc`將module移除時,==Multipass== session也跟著斷了: ```shell asahsieh@xiekaizhongs-MacBook-Pro ~ % multipass list list failed: ssh connection failed: 'Timeout connecting to 192.168.64.2' ``` - 如何找到crash前的log? > 在Multipass官方網站上的Docs,[Debugging section](https://multipass.run/docs/accessing-logs)裡有提到: You can also see instance logs here - the filename pattern is `<instance>-hyperkit.log`. - 擷取Call Trace如下: ```shell= 890 [ 233.311198] Call Trace: 891 [ 233.311675] ? is_ftrace_trampoline+0x9/0x20 892 [ 233.312576] BUG: unable to handle page fault for address: ffffffffc0649510 893 [ 233.314024] #PF: supervisor read access in kernel mode 894 [ 233.314995] #PF: error_code(0x0000) - not-present page 895 [ 233.316043] PGD 23c0e067 P4D 23c0e067 PUD 23c10067 PMD 6a725067 PTE 0 896 [ 233.317293] Oops: 0000 [#9] SMP PTI 897 [ 233.317963] CPU: 0 PID: 5691 Comm: pgrep Tainted: G OE 5.4.0-65-generic #73-Ubuntu 898 [ 233.320012] Hardware name: BHYVE, BIOS 1.00 03/14/2014 899 [ 233.321203] RIP: 0010:ftrace_ops_trampoline+0x7/0x50 900 [ 233.322143] Code: e8 6e 8a f0 ff 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 40 88 3d e9 96 53 01 c3 0f 1f 84 00 00 00 0 901 [ 233.325843] RSP: 0018:ffffaefb00aa6058 EFLAGS: 00010046 902 [ 233.326830] RAX: ff ``` - 從`BUG`訊息,可以看到CPU在存取位置`0xffffffffc0649510`時發生page fault但MMU無法處理, 並產生exception;接著CPU會將此位置存在**CR2**,CR2是一個CPU control register. > 延伸閱讀: [5\. Kernel level exception handling](https://www.kernel.org/doc/html/latest/x86/exception-tables.html?highlight=cr2) - 嘗試利用**Kdump**來debug - References: 1. [Oops! Debugging Kernel Panics](https://www.linuxjournal.com/content/oops-debugging-kernel-panics-0) 2. [Documentation for Kdump - The kexec-based Crash Dumping Solution ](https://www.kernel.org/doc/html/latest/admin-guide/kdump/kdump.html?highlight=cr2) ```shell [22:54] ubuntu@ubuntu:~$ sudo systemctl status kdump-tools.service Aug 07 22:48:01 ubuntu systemd[1]: Starting Kernel crash dump capture service... Aug 07 22:48:02 ubuntu kdump-tools[760]: Starting kdump-tools: Aug 07 22:48:02 ubuntu kdump-tools[816]: * no crashkernel= parameter in the kernel cmdline ``` - 設定crashkernel會透過**GRUB**於boot時設定,故我們想知道為什麼在multiplass上的ubuntu為什麼不會將更新的`grub.cfg`設定起來;於是透過google搜尋`multipass grub.cfg`,可以找到multipass github上有一個類似的open issue:[\[OSX\] Multipass ubuntu won't boot into newly installed kernel #1396](https://github.com/canonical/multipass/issues/1396#issue-573124519) > 一位collaborator, Saviq, 回覆[此問題是default backend on macOS - hyperkit的限制, 可改用VirtualBox backend](https://github.com/canonical/multipass/issues/1396#issuecomment-592917078); 改用`VirtualBox` backend後執行`rmmod`不會造成系統panic. > [Using VirtualBox in Multipass on macOS](https://multipass.run/docs/using-virtualbox-in-multipass-macos): `sudo multipass set local.driver=virtualbox` - ```shell RIP: ftrace_ops_trampline+0x7/0x50 ``` **RIP** stands for *Return Instruction Pointer* - Introduction of [Trampolines](https://eli.thegreenplace.net/2017/on-recursion-continuations-and-trampolines/) from [你所不知道的C語言:遞迴呼叫篇](https://hackmd.io/@unknowntpo/B1WzhkvKL/%2F%40sysprog%2Fc-recursion) ### 作答 延伸問題: #### 1. 解釋上述程式碼運作原理,包含 [ftrace](https://www.kernel.org/doc/Documentation/trace/ftrace.txt) 的使用 > 搭配閱讀 Book `The Linux Kernel Module Programming Guide`, 以下簡稱*LKMPG* > 參考到[sysprog 2019q1 HW2 \<Laurora\>同學的作答頁](https://hackmd.io/@4MNtoYSvT-qMsQIa3O1-jA/BkBpxL9LV?type=view) ##### Source code with explaination ```c= #include <linux/module.h> ... module_init(_hideproc_init); module_exit(_hideproc_exit); ``` - `module.h`: *Dynamic loading of modules into the kernel.* - file location: */usr/src/linux-headers-5.4.0-81/include/linux/module.h* - `module_init(x)`與`module_exit(x)` macros定義於此; 其中, 這兩個macro使用的變數`x`(@x)定義為: - 用於module_init(): function to be run at kernel boot time or module insertion - 用於module_exit(): function to be run when driver is removed - ==延伸閱讀==:**Chap 0.4.1 The Simplest Module**[^Chap0.4.1inlkmpg] 及 **0.4.2 Hello and Goodbye** in book *LKMPG* - 在早期的kernel版本,只能使用固定名字**init_module**及**cleanup_module** functions, 於插入及移除module時呼叫 - [ ] (查詢歷史典故) 隨著linux更新,為了讓使用者可以使用自定義的functions, 新增兩個Macros(`module_init`與`module_exit`)wrap上述兩個functions, :question: 並將**init_module**置換成marco的變數x, `initfn`; 以`module_init`為例: ```c= /* Each module must use one module_init(). */ #define module_init(initfn) \ static inline initcall_t __maybe_unused __inittest(void) \ { return initfn; } \ int init_module(void) __copy(initfn) __attribute__((alias(#initfn))); ``` ```c= MODULE_LICENSE("GPL"); MODULE_AUTHOR("National Cheng Kung University, Taiwan"); ``` - 在open source world, 原始碼可以自由的使用、修改及分享,但必須遵循[Open Source Licenses](https://opensource.org/licenses)所規範的方式。 - ==實驗== 我們嘗試將指定license的code comment起來,`//MODULE_LICENSE("GPL");`, 並利用`dmesg`查訊執行結果, 可看到系統回報`module license 'unspecified'`的訊息: ```shell= [ 140.139554] hideproc: module license 'unspecified' taints kernel. [ 140.139571] Disabling lock debugging due to kernel taint [ 140.139615] hideproc: module verification failed: signature and/or required key missing - tainting kernel [ 140.139668] hideproc: Unknown symbol __class_create (err -2) [ 140.139684] hideproc: Unknown symbol register_ftrace_function (err -2) [ 140.139705] hideproc: Unknown symbol ftrace_set_filter_ip (err -2) [ 140.139717] hideproc: Unknown symbol kallsyms_lookup_name (err -2) [ 140.139728] hideproc: Unknown symbol device_create (err -2) ``` - 故可在該kernel module source code的開頭利用`linux/module.h`中定義好的marcos來描述此程式的相關資訊 - ==延伸閱讀==:**Chap 0.4.4 Licensing and Module Documentation** in book *LKMPG* 由`module_init(_hideproc_init);` 可得知此module的"start" function是`_hideproc_init`, 此function執行的動作如下: ```c= alloc_chrdev_region(...) ``` - 於kernel doc可以在`The Linux Kernel API`::`Char devices`找到[此API的定義](https://www.kernel.org/doc/html/latest/core-api/kernel-api.html?highlight=alloc_chrdev_region#c.alloc_chrdev_region): **Allocates a range of char device numbers**. The major number will be chosen dynamically, and returned (along with the first minor number) in **dev**. Returns zero or a negative error code. :::info **device** 於book *LKMPG*的xxx章節說明 // status ::: #### 2. 本程式僅在 Linux v5.4 測試,若你用的核心較新,請試著找出替代方案 > 2020 年的變更 [Unexporting kallsyms\_lookup\_name()](https://lwn.net/Articles/813350/) > [Access to kallsyms on Linux 5.7+ > ](https://github.com/h33p/kallsyms-mod) #### 3. 本核心模組只能隱藏單一 PID,請擴充為允許其 PPID 也跟著隱藏,或允許給定一組 PID 列表,而非僅有單一 PID #### 4. 指出程式碼可改進的地方,並動手實作 - 執行第二次`insmod`時,系統會crash ## Study ### [Linux 核心模組運作原理](/pfRzP9sxRYqYbXS6Tm0Ynw) - (from 鳥哥) 核心模組 (kernel module) 的用途 - 使用情境:若目前使用的核心尚未支援新安裝的硬體,不需要重新編譯一個新的核心 - 為了此緣故,Linux參考了µkernel[^second]的設計,將一些不常用的類似驅動程式的咚咚獨立出核心,編譯成為模組,然後,核心可以在系統正常運作的過程當中載入這個模組到核心的支援。 - 如此一來,在不需要更動核心的前提之下,只要編譯出適當的核心模組,並且載入他,此Linux distro就可以使用此新安裝的硬體了。 ## Note - How to kill `virtualbox` backend on host OS if `multipass` VM crashed? - ref.: https://lhcathome.web.cern.ch/virtualbox-crashes-and-does-not-respond-all-boinc-what-can-i-do - command: 1. `$ps aux | grep [VBoxHeadless|hyperkit]` 2. `$sudo kill -9 $PIDs` [^first]: sysprog課程內**Makefile語法和示範**章節裡[Makefile語法](https://hackmd.io/@sysprog/SySTMXPvl#Makefile-%E8%AA%9E%E6%B3%95)中解釋*makefile語法中之`<tab>`與空白*。 [^Chap0.4.1inlkmpg]: **Chap 0.4.1** in book *LKMPG*: The Simplest Module * Kernel module至少必須提供兩個functions: 1. a "Start" (initialization) function call **init_module()** 2. an "end" (cleanup) function called **cleanup_module()** 以上兩個functions分別會在執行`insmod`or`rmmod`command時被呼叫 :::info :bulb: 在kernel 2.3.13之後,"start"及"end" functions可以使用任意的名字(Reference lkmpg Section 2.3) ::: init_module()的主要用途是`在kernel註冊一個handler`;而cleanup_module()則是做反向的事,讓module的可以*safely*被移除 [^second]: **1.2.4 A monolithic design** in *Linux Kernel Scheduler Internals, Jim and contributors* ###### tags: `linux2021_summer`