# 2024q1 Homework6 (integration)
contributed by < `Lccgth` >
## 實驗環境
```shell
$ gcc --version
gcc (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i7-9700KF CPU @ 3.60GHz
CPU family: 6
Model: 158
Thread(s) per core: 1
Core(s) per socket: 8
Socket(s): 1
Stepping: 13
CPU max MHz: 4900.0000
CPU min MHz: 800.0000
BogoMIPS: 7200.00
Caches (sum of all):
L1d: 256 KiB (8 instances)
L1i: 256 KiB (8 instances)
L2: 2 MiB (8 instances)
L3: 12 MiB (1 instance)
NUMA:
NUMA node(s): 1
NUMA node0 CPU(s): 0-7
$ uname -r
6.5.0-28-generic
```
## 自我檢查清單
### 閱讀〈 [Linux 核心模組運作原理](https://hackmd.io/@sysprog/linux-kernel-module) 〉
#### 解釋 `insmod`
```shell
$ man insmod
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.
```
`insmod` 是一個用於將模組加載到 linux 核心中的命令。使用此命令時,首先需要準備好需要加載的模組,通常以 `.ko` 為副檔名。接著,系統會檢查此模組的所需的其他模組,並嘗試加載缺少的模組。然後,使用 `insmod` 命令將模組加載到核心中。加載模組後,系統會執行模組的初始化函式 (`module_init`),該函式負責初始化模組所需的資源,當加載完畢時,系統會將模組的資訊紀錄在核心中,包括模組的名稱、版本、作者等。此後,此模組可以被核心或其他模組所使用。
#### Linux 核心模組的符號 (symbol) 如何被 Linux 核心找到
根據 [kernel/moudle.c](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c) 的描述,`find_symbol` 函式用來尋找指定的符號,此過程中會使用到 `each_symbol_section` 逐一走訪核心模組的符號,而其中使用 `list_for_each_entry_rcu` 走訪已載入的模組。
#### MODULE_LICENSE 巨集指定的授權條款對核心有什麼影響
根據 [include/linux/module.h](https://github.com/torvalds/linux/blob/master/include/linux/module.h) 的描述,`MODULE_LICENSE` 巨集定義了幾種被標記為免費的授權,包括 `GPL、GPL v2、GPL and additional rights、Dual BSD/GPL、Dual MIT/GPL、Dual MPL/GPL`,以及一種非免費的授權 `Proprietary`,這些設定的原因有數個,首先是讓使用者可以透過 `modinfo` 查詢模組是否免費,其次可以讓 Linux 開發社群能夠忽略包含專有模組的錯誤報告,最後可以讓供應商可以根據自己的策略做出對應的處理。
#### 藉由 strace 追蹤 Linux 核心的掛載,涉及哪些系統呼叫和子系統
以 `hello.ko` 為例:
```shell
$ sudo strace insmod hello.ko
...
getcwd("/home/scream/hello", 4096) = 19
newfstatat(AT_FDCWD, "/home/scream/hello/hello.ko", {st_mode=S_IFREG|0664, st_size=112344, ...}, 0) = 0
openat(AT_FDCWD, "/home/scream/hello/hello.ko", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1", 6) = 6
lseek(3, 0, SEEK_SET) = 0
newfstatat(3, "", {st_mode=S_IFREG|0664, st_size=112344, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 112344, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7a2ffa0d4000
finit_module(3, "", 0) = 0
...
```
在掛載模組到核心的過程中,因為 kernel module 執行在 kernel space 中,而 `insmod` 是在 user space 的程序,所以需要呼叫管理記憶體的 system call,將 user space 的資料複製到 kernel space 中。
此外,核心會讀取一個檔案,在此為 `hello.ko`,並獲取其 file descriptor,傳至 `finit_module` 中,參考 [finit_module](https://linux.die.net/man/2/finit_module) 得知 `finit_module` 使用方法和 `init_module` 類似,差別在 `finit_module` 從 file descriptor 中讀取要掛載的模組,如此以來可以避免使用加密簽名的模組確認掛載模組的開銷。
>The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd. It is useful when the authenticity of a kernel module can be determined from its location in the file system; in cases where that is possible, the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided.
### 閱讀〈 [The Linux Kernel Module Programming Guide](https://sysprog21.github.io/lkmpg/) 〉
#### 紀錄疑惑
##### UEFI SecureBoot 是什麼 (1.7 Before We Begin)
參考 [SecureBoot](https://wiki.debian.org/SecureBoot),UEFI SecureBoot 是一種驗證機制,確保由 UEFI 韌體啟動的程式碼是可以被信任的,用於保護系統不被啟動過程早期加載的惡意程式碼攻擊,當系統啟用此設定時,任何嘗試執行不可信程式的操作都會被禁止,而驗證的過程是通過加密金鑰來實現的。
##### off-by-one error 是什麼 (5.5 Code space)
參考 [Off-by-one error](https://en.wikipedia.org/wiki/Off-by-one_error),這是一種邏輯上的錯誤,涉及一個數字與預期的值相差了 1,例如在迴圈的判斷中多進行了一次迭代或少做了一次迭代,舉例來說如果一個迴圈要計算 `i` 為 0 到 9 時的總和,但在條件中使用了 `i <= 10`,導致多計算一個元素,此錯誤會導致程式碼無法正確的存取陣列等資料結構中的元素,使得資料處理發生錯誤。