contributed by < shiang1212
>
留意各式細節 (如上方的空白字元),唯有重視小處並步步為營,方可挑戰原始程式碼規模超越三千七百萬行的 Linux 核心
ssheep773
SimonLee0316
明確指出是哪幾個 git commits,既然你認為這位學員做不好,那就做出一次好的給他看,這樣才有討論和學習的效果。
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
stevendd543
malloc
與 calloc
差異時,你將未初始化的 malloc
印出 0 的原因在於,讀取未被初始化的記憶體在 C 語言中屬於未定義的行為,因此在印出來的值是 garbage value 剛好是 0。參考資料首先宣告一個 element_t,並為其配置記憶體,若配置失敗則回傳 false。接著為該 element_t 的 char *value 配置記憶體,注意 malloc 的記憶體大小為 strlen(s) + 1,應該是考慮到字串結束符號 '\0。此時如果配置失敗,在則回傳 false 之前,記得要把剛剛配置好的記憶體釋放掉。接著使用 strcpy 將字串內容 s 複製給該 element_t 的 char *value,這樣就完成了新節點的宣告。
舉例這段來說,其實你想表達在配置記憶體的流程與注意事項,這是好的探討,但在漢語上有些不通順,當你回頭看自己的筆記時,會發現自己把簡單的邏輯敘述的太雜亂。
element_t
list_head
element_t
作為佇列中的基本元素(節點),該結構體的成員有 char *value
與 struct list_node list
,前者用來表示該節點裝著什麼字串,後者為一結構體裝著兩個 struct list_head
的指標,用來表示該元素的前一個與後一個節點記憶體位置。
改進你的漢語表達。
整體架構如下圖:
參考其他同學 的圖後,發現製圖方法仍有錯誤,
明確標注同學的 GitHub 帳號名稱。
應使用 HackMD 中的程式碼區塊,如下
```graphviz
(程式碼)
```
待修改
q_new
程式碼註解一律用美式英語書寫。
用語要精準,「正常」和「不正常」的分野對應於記憶體配置是如何?
已修正
第 3 行使用到 malloc
配置記憶體給 head
,在記憶體空間充足且先前行為不存在記憶體越界存取的情況,malloc
會配置一個大小為 struct list_head
位元組 (Byte) 的記憶體,並回傳一個指標指向該記憶體,但並不是每次 malloc
都會那麼順利。
access 的翻譯是「存取」,而非「訪問」(visit)。
改進你的漢語表達。
已修正
沒做到的事,不要輕易宣告。你認為上述是清晰、明確,且通順的漢語表達嗎?jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
前後關聯是什麼?
以下節錄自 cmd 執行 $ man malloc
之結果:
RETURN VALUE
The malloc() and calloc() functions return a pointer to the allocated memory, which is suitably aligned for any built-in type. On error, these functions return NULL. NULL may also be returned by a successful call to malloc() with a size of zero, or by a successful call to calloc() with nmemb or size equal to zero.
使用 malloc
配置記憶體,在成功配置的情況下,malloc
會回傳一個指標指向對應大小的記憶體位置;在配置失敗或 size 為 0 的情況下,malloc
會回傳 NULL。所以需要用第 4~5 行。
此外,malloc
的 man page 提到:
The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
相比使用 calloc
,在使用 malloc
配置記憶體時,該記憶體不會被初始化成 0。
好奇 malloc
丟出來的未初始化記憶體長怎樣,所以我寫了以下程式測試: (test.c
)
編譯並執行:
得到的結果:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
工程人員說話要精準,避免說「好像」。
不知道為什麼看起來好像初始化過,在猜會不會是沒被修改過的記憶體都是初始化為 0,或是 OS 和編譯器的關係,待研究。
sbrk()?
program break?
q_free
避免非必要的項目縮排 (即 *
),以清晰、明確,且流暢的漢語書寫。
已修正
若 head
為空,則代表沒有記憶體需要被釋放,所以 return。接下來使用 list.h
裡面提供的 list_for_each_entry_safe
走訪每個節點。再使用 q_release_element
回收該節點的記憶體。最後回收掉 head
本身。
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
q_insert_head
避免非必要的項目縮排 (即 *
),以清晰、明確,且流暢的漢語書寫。
已修正
沒做到的事,不要輕易宣告。你認為這份筆記以清晰、明確,且通順的漢語書寫嗎?jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
首先宣告一個 element_t
,並為其配置記憶體,若配置失敗則回傳 false。接著為該 element_t
的 char *value
配置記憶體,注意 malloc
的記憶體大小為 strlen(s) + 1
,應該是考慮到字串結束符號 '\0
。此時如果配置失敗,在則回傳 false 之前,記得要把剛剛配置好的記憶體釋放掉。接著使用 strcpy
將字串內容 s
複製給該 element_t
的 char *value
,這樣就完成了新節點的宣告。
準備好要新增的節點之後,使用 list.h
提供的 list_add
,將該element_t
串在 head
的前面,達到新增 element_t
於佇列前端之效果。
參考 wanghanchi,在執行字串複製時,他使用 memcpy
取代 strcpy
。
我查看 strcpy
的 man page
char *strcpy(char *dest, const char *src);
DESCRIPTION
The strcpy() function copies the string pointed to by src, including the terminating null byte ('\0'), to the buffer pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy. Beware of buffer overruns! (See BUGS.)
BUGS
If the destination string of a strcpy() is not large enough, then anything might happen. Overflowing fixed-length string buffers is a favorite cracker technique for taking complete control of the machine. Any time a program reads or copies data into a buffer, the program first needs to check that there's enough space. This may be unnecessary if you can show that overflow is impossible, but be careful: programs can get changed over time, in ways that may make the impossible possible.
總而言之,strcpy
會走訪 src
指向的字串的每個字元,在碰到空字元 '\0'
之前,逐 Byte 寫入 dest
。但 strcpy
缺乏寫入合法性的判斷,影響程式的穩健性 (robustness)。
而 string.h
裡有一個與 strcpy
相似的函式:char *strncpy(char *dest, const char *src, size_t n)
,以下為 strncpy 的 man page 提供的實作範例:
這個函式相較 strcpy
多了一個 size_t n
參數,用來表示要寫入 dest
的字元數量,在呼叫函式的時候就先決定好要寫入的字元數量,確保不會有 overflow 的情況發生。
q_insert_tail
如 q_insert_head
,把 list_add(&new->list, head)
改成 list_add_tail(&new->list, head)
即可。
q_remove_head
q_remove_head
、q_remove_tail
宣告一個 element_t
指標指向要刪除的節點 (head->next 或 head->prev)。確認 bufisze != 0
並將該節點的字串內容複製給 sp
,最後用 list_del
將該節點從鏈結串列中移除。
q_size
先判斷該鏈結串列是否為空,是的話 return 0。接著走訪該鏈結串列的每個節點並累計長度。
q_delete_mid
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
7cfd185 中的 q_delete_mid
,成功通過 2095. Delete the Middle Node of a Linked List,但完全無法應用在環狀雙向鏈結的場景下,於是我參考 wanghanchi 以及你所不知道的 C 語言: linked list 和非連續記憶體中的龜兔賽跑 (快慢指標) 方法。
什麼「核心」?本課程叫什麼名字,你還記得嗎?
已修正
想法如下:
慢指標 slow
一次走一步,快指標 fast
一次走兩步,fast
走的步數會是 slow
的兩倍,所以當 fast
走完鏈結串列的總長時,這時的 slow
只走了鏈結串列長度的一半,也就是走到鏈結串列的中間節點,正好是 q_delete_mid
所要刪除的節點。
而判斷 fast
是否走到環狀鏈結串列的尾端可以分成兩個情況:
鏈結串列的長度為奇數:
fast
從 head
出發後,每次走兩步,只會停在 head
和 Even 節點,而 fast
在走完鏈結串列的總長時,會剛好回到 head
,判斷 fast == head
即可確認fast
是否走到該環狀鏈結串列的尾端。
鏈結串列的長度為偶數:
這個情況 fast
只會停在 Even 節點,而最後一個 Even 節點正是 head->prev
,所以判斷 fast == head->prev
。
q_swap
參考 wanghanchi 的程式碼,兩個節點位置交換,簡單來說就是把後者取出,放到前者的前面,且因為是每兩個節點的交換,所以執行完交換後指標要往前兩個節點。
q_reverse
參考 randv 的程式碼,使用 list_for_each_safe
走訪每個節點,用 list_move
將每個節點移除並放在 head
前端。
q_reverseK
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
參考 ShchangAnderson 的作法,走訪每個節點,每 k
個節點作一次 q_reverse
,並用 list_splice_tail
從尾端存進一個新的串列。考慮到串列長度可能不整除 k
,所以在判第 13 行加入 ct == size
,保證剩餘的節點也能被處理到。
q_sort
參考 WangHanChi 的程式碼,採用 MergeSort 實作排序。
merge_two_nodes
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
使用間接指標,iter
負責找出目前擁有最小字串的節點是屬於哪個串列 (left
or right
),indirect
負責將 iter
指的節點存進 new_head
。
merge_divide
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
利用快慢指標找出鍊結串列中點,找到後從中點將串列切開,變成 head
和 middle
兩條串列,再遞迴將這兩條串列從中點切開,直到每條串列只剩一個節點。
q_sort
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
第 6、7 行將 head
與原本為排序的串列斷開,並使用 merge_divide
得到排序好的串列。但因為 merge_divide
回傳的串列為單向鏈結串列,所以必須走訪每個節點,建構其 *prev
。
q_ascend
、q_descend
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
該函式目的為刪除節點使該串列成為嚴格遞增/遞減串列。
這裡提到的數值遞增遞減,是指字串中逐字元的 ASCII 值,使用 strcmp
可以很好得到兩字串間字元的大小關係。
用兩個指標由後往前尋訪每個節點,當 strcmp(right->value, left->value) > 0
發生時,代表後節點的字串大於前節點的字串,兩節點為遞增關係。反之,當 strcmp(right->value, left->value) < 0
發生,代表前節點的字串大於後節點的字串,兩節點為遞減關係。
q_merge
不該先列出程式碼,相反的,你該先探討自己的想法、考慮因素,具體實施的方案,過程中可能會有副作用 (side effect),這都要涵蓋。這是工程素養之一,即有效的溝通。
參考 Kuanch 以及 WangHanChi 的說明。
根據要求,傳入的
head
為queue_contex_t
的head
,因此也會需要將節點往next
移動一格才開始存取每個queue
的head
。
首先來看 queue_contex_t
是什麼東西
q
用來指向佇列的 headchain
用來串連多個佇列的 headid
、size
紀錄該佇列的資訊TODO: 增加 *q
指向雙向鏈結串列
用 p
走訪這個 chain 的每個節點,合併每個節點指向的雙向鏈結串列至首個節點,最後再用 q_sort
將首個節點排序,實現合併並且排序的效果。
知道了!
在終端機執行
make valgrind
trace-17 跑了很久,執行結果尚無找到記憶體問題。
這篇論文提出時序洩漏檢測 (timing leakage detection) 工具 dudect,用以評估程式碼在特定平台上是否能在常數時間內運行完畢。
現有的方法大多需要模擬硬體行為,而硬體製造商釋出的硬體資訊卻少之又少,十分不利於檢測工具的開發。這篇論文基於以上問題提出改善方案,提出的 dudect 檢測工具不使用靜態程式分析 (static analysis),而是使用對於時間的統計分析 (statistical analysis),藉此避免測量時間的方法對於硬體產生依賴性。
時序攻擊 (timing attack) 是旁路攻擊的一種,攻擊者透過加密算法所需的執行時間推斷敏感訊息,導致安全漏洞。舉例來說,若加密算法在判斷密碼是否正確時,是使用逐字元的判斷並在偵測字元不匹配時回傳,攻擊者在多次嘗試並觀察演算法的執行時間,判斷輸入字元是否匹配成功,進而推得出密碼。
為了避免時序攻擊,可以使用時序洩漏偵測工具,或是使用常數時間演算法來處理敏感資訊,將時間差異最小化,甚至可以引入隨機元素,使攻擊者難以通過觀察時間模式來推斷信息。
時序洩漏偵測 (Timing leakage detection) 用於檢測演算法是否存在時序洩漏的風險,以此防止時序攻擊。通常會分析程式的執行模式,包括時間消耗、記憶體存取模式、CPU使用率等。透過觀察這些模式的變化,以發現可能存在的時序泄漏漏洞。
Measure execution time
測量兩個不同類別的輸入資料在加密函式上的執行時間,並得到兩個數據分布。
疑問:不清楚 c) 的每個測量都對應一個隨機的類別是什麼意思,目前想法是每次測量都使用隨機的輸入資料類別,但這又和減少環境差異有什麼關係呢?
Apply post-processing
得到數據分布後,可以對其進行資料後處理。
a) Cropping
對於較長的執行時間,大部分的時間分佈都是呈 positively skewed distribution,可能是因為測量誤差 (measurement artifacts),測量函式會受到 OS 或者外部程式影響。這種情況下,可以裁剪特定測量值以便後續檢定。
b) Higher-order preprocessing
依照不同的假說檢定應用,使用更高階的資料預處理。
Apply statistical test
使用假說檢定試圖推翻虛無假說:「兩個數據分布是相同的」,驗證對立假說:「兩個數據分布不是相同的」,藉此判斷該加密函式是否存在時序洩漏問題。
Student's t-distribution,簡稱 t 分布,t 分布為一連續對稱分布,t 分布與期望值為0變異數為1常態分布特徵相似(鐘形、對稱於平均數 0、數值向兩側延伸)。在樣本數小或母體標準差未知的情況下,使用樣本標準差取代母體標準差,並使用 t 分布進行推論。
t 分布定義了一個參數:自由度(degrees of freedom),隨著自由度 越大,t 分布的分布狀況越接近常態分布,當自由度逼近無限時,t 分布就會退化成常態分布。
Student's t-distribution 可以用於假設檢定上,在樣本數過小或母體標準差未知的情況下,通常會比常態分布更適合用於假設檢定。
沒做到的事,不用裝忙。
參考 jimmylu890303 的作法,在 trace-17 中,會檢查 q_insert_head
、q_insert_tail
、q_remove_head
、q_remove_tail
是否能在常數時間內完成。
首先,在 fixture.h
,可以看到 is_##x##_const()
的宣告:
查閱 C 語言規格書,針對 Concatenation 進行解說,使用規格書裡頭的術語。
TODO:C99 6.10.3.3 The ## operator
在 ,"##" 表示的是連接運算子。以下為例子:#define
中
其中的 DUT_FUNCS
又可以在 constant.h
裡看到:
在 qtest.c
中,當 simulation == 1
時會呼叫 is_##x##_const
。
又可以在 fixture.c
看 is_##x##_const()
的實作,發現最後會呼叫 test_const(#op, DUT(op))
使用 test_const(#op, DUT(op));
進行測試。
在 oreparaz/dudect 的程式碼具備 percentile 的處理,但在 lab0-c 則無。
TODO:參考 willwillhi、Wufangni
Fisher-Yates Shuffle 是由 Fisher 和 Yates 提出的亂序演算法,該演算法的實作概念:從最後一個元素開始,將每個元素與其前面隨機一個元素交換。
要在 lab0-c 裡實作 Fisher-Yates Shuffle,首先我們需要做出 swap function,這裡的 swap function 跟佇列操作裡的 q_swap
不同,q_swap
做的是前後兩個元素的交換,swap function 要做的是任意兩個節點的交換。