# `simrupt` 研究和改進 contributed by < `Build-A-Moat` > ## simrupt 流程圖 ```graphviz digraph ele_list{ rankdir=TD; node[shape=record]; timer_handler [label="timer_handler|mod_timer", style="bold", color=bold]; process_data [label="process_data", style="bold"]; tasklet_schedule [label="tasklet_schedule", style="bold"]; fast_buf [label="fast_buf|fast_buf_get|fast_buf_put", style="bold"]; update_simrupt_data [label="update_simrupt_data", style="bold"]; simrupt_tasklet_func [label="simrupt_tasklet_func", style="bold"]; queue_work [label="queue_work", style="bold"]; simrupt_work_func [label="simrupt_work_func", style="bold"]; produce_data [label="produce_data", style="bold"]; kfifo [label="kfifo|kfifo_in|kfifo_to_user", style="bold"]; simrupt_read [label="simrupt_read", style="bold"]; timer_handler -> process_data; process_data -> update_simrupt_data; process_data -> tasklet_schedule; update_simrupt_data -> fast_buf:fast_buf_put; tasklet_schedule -> simrupt_tasklet_func; simrupt_tasklet_func -> queue_work; queue_work -> simrupt_work_func; simrupt_work_func -> fast_buf:fast_buf_get; fast_buf:fast_buf_get -> produce_data; produce_data -> kfifo:kfifo_in; simrupt_read -> kfifo:kfifo_to_user; } ``` ## tasklet 在 [Modernizing the tasklet API](https://lwn.net/Articles/830964/) 中指出,將 tasklet 的 callback function 從 `void (*func)(unsigned long)` 改為 `void (*callback)(struct tasklet_struct *)`,這樣可以改善 - 可以 re-entry - 避免額外使用空間儲存 unsigned long - (待確定翻譯)不能使用 `no type checking` 在此 argument - (No type checking cannot be performed on this argument. Instead of using container_of() like other callback subsystems, it forces callbacks to do explicit type cast of the unsigned long argument into the required object type.) - 避免 Buffer overflows 覆寫 .func 與 .data 所導致的攻擊 ### 問題 - `void (*callback)` 為什麼需要使用 callback function、需要參數的用意為何? - 若參數是用來辨認哪個 tasklet,原本的 unsigned long 是否就不能重複? > 修改參數用意:因應 linux kernel 往 hard real time 發展,這樣改寫可以 re-entry - 從 [linux/interrupt.h](https://elixir.bootlin.com/linux/latest/source/include/linux/interrupt.h#L624) 的註解中可以看到 tasklet_struct 是已經被棄用的 API,所以歷史發展是為了 hard real time 改為可以 re-entry,但 thread interrupt 更好,所以棄用 tasklet 嗎? - Buffer overflows 可以覆寫 .func 與 .data,那也可以覆寫 `*callback` 及 `struct tasklet_struct *`,這樣改就不會造成攻擊嗎? 還是能提昇攻擊難度? - [simrupt](https://github.com/sysprog21/simrupt/blob/main/simrupt.c) 中的20行 DECLARE_TASKLET(arg1, arg2, 0L) 在 [linux/interrupt.h](https://elixir.bootlin.com/linux/latest/source/include/linux/interrupt.h#L660) 中的660行沒有第3個參數 ``` DEVICE OPERATIONS ----------------- Some devices present their control interfaces as collections of memory locations, but the order in which the control registers are accessed is very important. For instance, imagine an ethernet card with a set of internal registers that are accessed through an address port register (A) and a data port register (D). To read internal register 5, the following code might then be used: *A = 5; x = *D; but this might show up as either of the following two sequences: STORE *A = 5, x = LOAD *D x = LOAD *D, STORE *A = 5 the second of which will almost certainly result in a malfunction, since it set the address _after_ attempting to read the register. ``` - 測試 [The Linux Kernel Module Programming Guide](https://sysprog21.github.io/lkmpg/#tasklets) 中的 tasklet 範例,結果與範例不符,可能是 mdelay 沒有運作? ``` [2411911.418468] tasklet example exit [2411912.836813] tasklet example init [2411912.836828] Example tasklet starts [2411917.798345] Example tasklet ends [2411917.996831] Example tasklet init continues... [2412072.528881] tasklet example exit [2412081.750219] tasklet example init [2412081.751096] Example tasklet starts [2412086.712335] Example tasklet ends [2412086.909791] Example tasklet init continues... ``` #### TODO - tasklet 特性 > 相較於 workqueue,tasklet - 每0.1秒輸出一個字元 > 使用 mod_timer(),將 delay 設置為 100ms,再用 msecs_to_jiffies 將 delay 轉為 jiffies > 為什麼要用 jiffies? user space, process 會 block,kernel 可以做其他事情 > mdelay 會實際消耗 cpu 週期,需小心使用。 - 如何消耗 > simrupt_work_func() 會使用 fast_buf_get() 將 circ_buf 中的值取出,再用 produce_data() 將 data 放入 kfifo,最後 simrupt_read() 用 kfifo_to_user() 將 kfifo 的值輸出到 user space。 - tasklet 時間拉到很長 - CMWQ - 哪些 mb 是必須的,需要再改進,需實做。 > fast_buf_get() 中的 smp_mb() 與 fast_buf_put() 中的 smp_wmb(),在移動 circ_buf 中的 head 與 tail 之前,都必須要有 memory barrier,否則 fast_buf_get() 可能會得到錯誤的 ret,fast_buf_put() 可能會放錯位置。 - The Linux Kernel Module Programming Guide 建立 tasklet issue - why ring buffer? > 要宣告特別大小的buffer,且控制頭尾會很難 coding,使用 ring buffer,可以更簡潔且方便。 > 若用 array 要傳,需要更多參數。 - simrupt 中 delay 縮小,看時間是否還是準確?目前優勢是不太佔用 cpu 資源,不同時間刻度大小,適合哪些方法? ### kfifo [linux/kfifo.h](https://elixir.bootlin.com/linux/latest/source/include/linux/kfifo.h#L44) 中有定義 struct kfifo ```c struct __kfifo { unsigned int in; unsigned int out; unsigned int mask; unsigned int esize; void *data; }; ``` #### reference [内核数据结构 —— 内核队列 (kfifo)](https://blog.csdn.net/zhoutaopower/article/details/86491852)