# 2021q1 Homework1 (lab0)
contributed by < `Uduru0522` >
> GitHub: [lab0-c](https://github.com/Uduru0522/lab0-c)
> 去年已經寫過,但也只有寫,這次目標為補上其他作業需求
> TODO: 附上 2020 年秋季作業的超連結,並標註本次作業新增部分
> :notes: jserv
## 修正開啟 Address Sanitizer 後出現的錯誤
照著作業指示進行 `make SNITIZER=1` 編譯後,執行 help 命令會出現如下的錯誤 :
```
==4344==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5626b775c400 at pc 0x5626b7745905 bp 0x7ffe6ed6e440 sp 0x7ffe6ed6e430
READ of size 4 at 0x5626b775c400 thread T0
#0 0x5626b7745904 in do_help_cmd /home/uduru/GitHub-Repos/lab0-c/console.c:307
#1 0x5626b7745a18 in interpret_cmda /home/uduru/GitHub-Repos/lab0-c/console.c:221
#2 0x5626b77461fd in interpret_cmd /home/uduru/GitHub-Repos/lab0-c/console.c:244
#3 0x5626b7747940 in run_console /home/uduru/GitHub-Repos/lab0-c/console.c:660
#4 0x5626b7744527 in main /home/uduru/GitHub-Repos/lab0-c/qtest.c:788
#5 0x7fa8cf0100b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#6 0x5626b7741b8d in _start (/home/uduru/GitHub-Repos/lab0-c/qtest+0x8b8d)
0x5626b775c401 is located 0 bytes to the right of global variable 'echo' defined in 'console.c:59:13' (0x5626b775c400) of size 1
```
閱讀錯誤訊息得知,在執行 add_param() 時,發生了 global buffer overflow.
這個錯誤是由於在傳入各個參數時,我們為了在型別 `int*` 的位置傳入型態為 `static bool` 的 `echo`, 我們進行了 `(int *) &echo` 的轉型,然而由於 `echo` 僅使用 1 byte 的空間,我們卻將其當作 `int` (4-bytes)來讀去,超出範圍。在同樣是 `bool` 型別的 `simulation` 也可以發現一樣的錯誤。
:::info
根據 C99 規格書 $\S$ 6.2.5:
> An object declared as type _Bool is large enough to store the values 0 and 1.
可以得知這個錯誤不一定會在每個系統發生。
:::
> TODO: 用 gdb 確認 `_Bool` 實際用多寬的資料型態來表示。乍看 `_Bool` 只包含 `0` 和 `1` 這兩種可能,為何不定義為 `char` 型態呢?請思考實作的議題。
> :notes: jserv
:::warning
在廁所亂看 SO 時發現這個 QA:
[What are shadow bytes in Address Sanitizer and how should I interpret them?](https://stackoverflow.com/questions/61674317/what-are-shadow-bytes-in-addresssanitizer-and-how-should-i-interpret-them?rq=1)
預計再補上 Shadow Byte 相關的資料。
:::
### 修正方法 1 - 改變變數型態
在整專案中搜尋,可以發現 `echo` 被使用時並未有其他地方其必須為 `_Bool` 型別,因此簡單的方法是**直接將變數宣告為 `int`**,此 buffer overflow 並不會出現。
同樣的方法也可以套用在 `bool simulation` 上。
> 發現問題不在這裡,而是 `printf()` 中的 `%d` 導致讀取超出 buffer.
>> ### ~~修正方法 2 - 增加 `seter_function` 參數~~
>> ~~由於我個人認為直接將變數型別修改為整數反而破壞 `_Bool` 給人的『只有開跟關兩個狀態』的一目了然特性,卻想不太出來有何修正方法不需要改變型態,我直接跑到 SO 問了以下問題 :~~
>>> [Prevent global buffer overflow casting a static bool reference to int pointer](https://stackoverflow.com/questions/66504085/prevent-global-buffer-overflow-casting-a-static-bool-reference-to-int-pointer/66504366#66504366)
## `_Bool` 研究
經過在 gdb 中我們可以得知 `_Bool` 型別的大小 :
```cpp
(gdb) print sizeof(_Bool)
$1 = 1
```
### 為了滿足型別轉換規定
最先,為了釐清為什麼 `_Bool` 是 1 個位元組的寬度,首先,我查詢了 C99 標準中對 `Bool` 型別相關的規定並在 §6.3 的型別轉換相關的內容發現 ^(展開是C99原文)^:
:::spoiler §6. 3. 1. 1 Boolean Characters And Integers
> Every integer type has an integer conversion rank defined as follows:
— No two signed integer types shall have the same rank, even if they hav e the same representation.
— The rank of a signed integer type shall be greater than the rank of any signed integer type with less precision.
— The rank of long long int shall be greater than the rank of long int, which shall be greater than the rank of int, which shall be greater than the rank of short int, which shall be greater than the rank of signed char.
— The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any.
— The rank of any standard integer type shall be greater than the rank of any extended integer type with the same width.
— The rank of char shall equal the rank of signed char and unsigned char.
— The rank of _Bool shall be less than the rank of all other standard integer types.
— The rank of any enumerated type shall equal the rank of the compatible integer type
(see 6.7.2.2).
— The rank of any extended signed integer type relative to another extended signed integer type with the same precision is implementation-defined, but still subject to the other rules for determining the integer conversion rank.
— For all integer types T1, T2, and T3, if T1 has greater rank than T2 and T2 has greater rank than T3, then T1 has greater rank than T3.
:::
- 所有整數型別都有一個轉換階級(conversion rank)。
- 較高精度的整數型別必須有相對較高的階級。
- 階級必須符合 `long long int` > `long int` > `int` > `short int` > `signed char` 的關係
- 所有有號整數必須有不同的階級,即使他們的精度相同。
- **`_Bool` 的階級必須比其他所有標準整數型別 (standard integer types) 還要低**。
如果綜合第二及第四點,我們可以推測出 **`_Bool` 必須只使用小於等於所有其它型別的記憶體空間**,綜合 §6.2.6.1 第四點所示,
> Values stored in non-bit-field objects of any other object type consist of $n \times$ `CHAR_BIT` bits, where n is the size of an object of that type, in bytes.
`CHAR_BIT` 又被定義為 (§5.2.4.2.1)
> number of bits for smallest object that is not a bit-field (byte)
因此,`_Bool` 的大小不能大於 `char`,`char` 的大小又被定義與 `CHAR_BIT` 相同的狀況下,就只剩下能擁有 1 位元組大小的空間。
## TODO Lists
> Add information about shadow bytes in address sanitizer