# 2021q1 Homework1 (lab0) contributed by < `tigger12613` > > [作業要求](https://hackmd.io/@sysprog/linux2021-lab0) ## 基本實作 詳細的程式內容請參見 [GitHub](https://github.com/tigger12613/lab0-c) ### Queue structure ``` c typedef struct { list_ele_t *head; list_ele_t *tail; int size; } queue_t; ``` * tail: 指向 queue 的尾端,目的是為了 $O(1)$ 的 insert tail * size: 計算 queue 的大小,目的是為了 $O(1)$ 得到 queue size ### New 新增一個 queue 並且初始化數據 time complexity = $O(1)$ ### Free 從頭開始一個一個刪除 element 以及 element 裡的 value 記得要先確認 queue 的存在。然後一個一個 time complexity = $O(n)$ ### Insert head/tail 新增一個 element 並且放入 value , 把 element 放到queue的head/tail time complexity = $O(1)$ * 我的失誤 * 所有 pointer 都要自己寫入 NULL , c pointer 並沒有 default value * 如果第二個 malloc 失敗,要記得 free 第一個 allocated space * strncpy 並不一定會加上 \0 要手動加上 * 如果 queue 為空,要同時改動head跟tail ### Remove head 刪除 head 指向的 element , head 指向下一個 element time complexity = $O(1)$ ### Queue size 回傳 queue 的長度,如果沒有 queue return 0 因為有紀錄 size ,所以 time complexity = O(1) ### Reverse queue 反轉 queue time complexity = $O(n)$ ### Sort 我選擇使用 merge sort [參考資料](https://npes87184.github.io/2015-09-12-linkedListSort/) 經過 merge sort 之後 tail 會跑掉,要重新找到 tail time complexity = $O(nlogn)$ ``` c void q_sort(queue_t *q) { if (!q || !q->head) { return; } q->head = merge_sort(q->head); while (q->tail->next) { q->tail = q->tail->next; } } list_ele_t *merge(list_ele_t *left, list_ele_t *right) { list_ele_t *head = NULL; list_ele_t *tail = NULL; while (left && right) { if (strcmp(left->value, right->value) <= 0) { if (!head) { head = left; tail = left; } else { tail->next = left; tail = left; } left = left->next; } else { if (!head) { head = right; tail = right; } else { tail->next = right; tail = right; } right = right->next; } } if (left) { tail->next = left; } if (right) { tail->next = right; } return head; } list_ele_t *merge_sort(list_ele_t *head) { if (!head || !head->next) { return head; } list_ele_t *fast = head->next, *slow = head; while (fast && fast->next) { fast = fast->next->next; slow = slow->next; } fast = slow->next; slow->next = NULL; list_ele_t *left = merge_sort(head); list_ele_t *right = merge_sort(fast); return merge(left, right); } ``` ## 小結 通過測資 100/100 ## Qtest help error 第一步重現錯誤 ``` shell $ make clean $ make SANITIZER=1 $ ./qtest cmd> help Commands: # ... | Display comment free | Delete queue help | Show documentation ih str [n] | Insert string str at head of queue n times. Generate random string(s) if str equals RAND. (default: n == 1) it str [n] | Insert string str at tail of queue n times. Generate random string(s) if str equals RAND. (default: n == 1) log file | Copy output to file new | Create new queue option [name val] | Display or set options quit | Exit program reverse | Reverse queue rh [str] | Remove from head of queue. Optionally compare to expected value str rhq | Remove from head of queue without reporting value. show | Show queue contents size [n] | Compute queue size n times (default: n == 1) sort | Sort queue in ascending order source file | Read commands from source file time cmd arg ... | Time command execution Options: ================================================================= ==12203==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55fc196ec3c0 at pc 0x55fc196d57bd bp 0x7fff4e970150 sp 0x7fff4e970140 READ of size 4 at 0x55fc196ec3c0 thread T0 #0 0x55fc196d57bc in do_help_cmd /home/tigger/linux2021/lab0-c/console.c:307 #1 0x55fc196d58d0 in interpret_cmda /home/tigger/linux2021/lab0-c/console.c:221 #2 0x55fc196d60b5 in interpret_cmd /home/tigger/linux2021/lab0-c/console.c:244 #3 0x55fc196d77f8 in run_console /home/tigger/linux2021/lab0-c/console.c:660 #4 0x55fc196d43e5 in main /home/tigger/linux2021/lab0-c/qtest.c:780 #5 0x7ff8d7dae0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) #6 0x55fc196d1b8d in _start (/home/tigger/linux2021/lab0-c/qtest+0x8b8d) 0x55fc196ec3c1 is located 0 bytes to the right of global variable 'echo' defined in 'console.c:59:13' (0x55fc196ec3c0) of size 1 SUMMARY: AddressSanitizer: global-buffer-overflow /home/tigger/linux2021/lab0-c/console.c:307 in do_help_cmd Shadow bytes around the buggy address: 0x0ac0032d5820: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 0x0ac0032d5830: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9 0x0ac0032d5840: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 0x0ac0032d5850: 04 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00 0x0ac0032d5860: 00 00 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9 =>0x0ac0032d5870: 01 f9 f9 f9 f9 f9 f9 f9[01]f9 f9 f9 f9 f9 f9 f9 0x0ac0032d5880: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 0x0ac0032d5890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ac0032d58a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ac0032d58b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ac0032d58c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc ==12203==ABORTING ``` 由上面的提示來看應該是下面這段程式出了問題 ``` c while (plist) { /*這行有錯誤*/ report(1, "\t%s\t%d\t%s", plist->name, *plist->valp, plist->documentation); plist = plist->next; } ``` 使用 valgrind 分析,出現預期外的問題 ```shell= $make clean $make SANITIZER=1 $valgrind -q --leak-check=full ./qtest ==24379==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD. ``` 如果在 make 的時候不加上 SANITIZER=1 ,valgrind並未發現記憶體問題 ```shell= $make clean $make $valgrind -q --leak-check=full ./qtest cmd> help Commands: # ... | Display comment free | Delete queue help | Show documentation ih str [n] | Insert string str at head of queue n times. Generate random string(s) if str equals RAND. (default: n == 1) it str [n] | Insert string str at tail of queue n times. Generate random string(s) if str equals RAND. (default: n == 1) log file | Copy output to file new | Create new queue option [name val] | Display or set options quit | Exit program reverse | Reverse queue rh [str] | Remove from head of queue. Optionally compare to expected value str rhq | Remove from head of queue without reporting value. show | Show queue contents size [n] | Compute queue size n times (default: n == 1) sort | Sort queue in ascending order source file | Read commands from source file time cmd arg ... | Time command execution Options: echo 1 Do/don't echo commands error 5 Number of errors until exit fail 30 Number of times allow queue operations to return false length 1024 Maximum length of displayed string malloc 0 Malloc failure probability percent simulation 0 Start/Stop simulation mode verbose 4 Verbosity level ``` 問題出現在 `console.c` 的下面這兩行的 `simulation`, `echo` 都是 `bool` 變數,在轉換成 `int` 會有長度不一樣的問題, `sizeof(int)` = 4, `sizeof(bool)` = 1 ``` cpp=104 add_param("simulation", (int *) &simulation, "Start/Stop simulation mode",NULL); ``` ``` cpp=108 add_param("echo", (int *) &echo, "Do/don't echo commands", NULL); ``` 我的第一個想法是把 `bool` 轉成 `int` ,但是這種作法無法釋放新增的記憶體,會導致 memory leak ```cpp=673 int* btoi(bool b){ int *tmp = malloc(sizeof(int)); if(!tmp){ return NULL; } *tmp = b; return tmp; } ``` ``` cpp=104 add_param("simulation", btoi(simulation), "Start/Stop simulation mode",NULL); ``` ``` cpp=108 add_param("echo", btoi(echo), "Do/don't echo commands", NULL); ``` ### Solution 接著我想就直接更改 `simulation`, `echo` 的 type ```cpp=11 extern bool simulation; ``` ```cpp=21 bool simulation = 0; ``` ```cpp=59 static bool echo = 0; ``` ```cpp=11 extern int simulation; ``` ```cpp=21 int simulation = 0; ``` ```cpp=59 static int echo = 0; ``` 更改過後,執行順利,也正確的釋放記憶體 ```shell $ make clean $ make SANITIZER=1 $ ./qtest cmd> help Commands: # ... | Display comment free | Delete queue help | Show documentation ih str [n] | Insert string str at head of queue n times. Generate random string(s) if str equals RAND. (default: n == 1) it str [n] | Insert string str at tail of queue n times. Generate random string(s) if str equals RAND. (default: n == 1) log file | Copy output to file new | Create new queue option [name val] | Display or set options quit | Exit program reverse | Reverse queue rh [str] | Remove from head of queue. Optionally compare to expected value str rhq | Remove from head of queue without reporting value. show | Show queue contents size [n] | Compute queue size n times (default: n == 1) sort | Sort queue in ascending order source file | Read commands from source file time cmd arg ... | Time command execution Options: echo 1 Do/don't echo commands error 5 Number of errors until exit fail 30 Number of times allow queue operations to return false length 1024 Maximum length of displayed string malloc 0 Malloc failure probability percent simulation 0 Start/Stop simulation mode verbose 4 Verbosity level cmd> quit Freeing queue ```