# 2018q3 Homework3 ([review](https://hackmd.io/s/rywcBOy9Q))
contributed by < [wingard](https://github.com/wingard) >
###### tags: `HW3`
## 作業要求
* 從 [第 1 週](https://hackmd.io/s/S1a9-YO_Q), [第 2 週](https://hackmd.io/s/BJA6LjbK7), [第 3 週](https://hackmd.io/s/BkyQskjK7) 選出你感興趣的 5 個題組進行作答,並且回答附帶的「延伸問題」
* 比照 [課前測驗參考解答: Q1](https://hackmd.io/s/ByzoiggIb) 和 [對 Linked List 進行氣泡排序](https://hackmd.io/s/BklGm1MhZ) 的模式來撰寫,需要詳細分析自己的想法、參閱的材料 (包含 C 語言規格書的章節),以及各式實驗
* 若有不能理解的部分,請標註出來。善用 HackMD 的語法 `:::info` 和 `:::` 標註你的提問
:::info
像是這樣標註提問
:::
---
## [第 2 週 測驗 1](https://hackmd.io/s/BJA6LjbK7)
考慮到以下程式碼:
```C
const char *p;
void dont_do_this(void) {
const char c_str[] = "This will change";
p = c_str;
}
```
指出存在的問題和提出修正機制,需要用 C99/C11 規格解釋。
:::success
延伸問題: 在 Common Vulnerabilities and Exposure](https://cve.mitre.org/) (CVE) 找出類似上述不當的 string literal 操作,而導致的安全漏洞,並加以探討
:::
---
* 參考 C99/C11 6.2.4 Storage Duration of objects
> The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, 25) and retains its last-stored value throughout its lifetime. 26) **If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.**
會讓 pointer p 在離開 function 之後變成一個 undefined behavior
延伸問題
:::info
在CVE上搜尋string literal 似乎找不到類似的 UB 漏洞,再想想...><
:::
---
## [第 2 週 測驗 2](https://hackmd.io/s/BJA6LjbK7)
[指標篇](https://hackmd.io/s/HyBPr9WGl) 提到 signal 系統呼叫的原型宣告:
```C
void (*signal(int sig, void (*handler)(int))) (int);
```
該如何解析呢?
提示: 參閱 manpage: [signal(2)](http://man7.org/linux/man-pages/man2/signal.2.html)
:::success
延伸問題: 解釋 signal(2) 的作用,並在 GitHub 找出應用案例
:::
---
signal 宣告的解析,可參考 [StackOverflow](https://stackoverflow.com/a/15742688) 上的解釋
> signal is a function with a parameter named sig of type `int`, and a parameter named handler which is a pointer to a function taking an `int` parameter and returning `void`, returning a pointer to a function taking an `int` parameter and returning `void`.
```
signal -- signal
signal( ) -- is a function
signal( sig ) -- with a parameter named sig
signal(int sig, ) -- of type int
signal(int sig, handler ) -- and a parameter named handler
signal(int sig, *handler ) -- which is a pointer
signal(int sig, (*handler)( )) ) -- to a function
signal(int sig, (*handler)(int)) ) -- taking an int parameter
signal(int sig, void (*handler)(int)) ) -- and returning void
*signal(int sig, void (*handler)(int)) ) -- returning a pointer
( *signal(int sig, void (*handler)(int)) )( ) -- to a function
( *signal(int sig, void (*handler)(int)) )(int) -- taking an int parameter
void ( *signal(int sig, void (*handler)(int)) )(int); -- and returning void
```
signal 的用途參考 signal(2)
> signal() sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL, or the address of a programmer-defined function (a "signal handler").
> If the signal signum is delivered to the process, then one of the following happens:
> * If the disposition is set to SIG_IGN, then the signal is ignored.
> * If the disposition is set to SIG_DFL, then the default action
associated with the signal (see signal(7)) occurs.
> * If the disposition is set to a function, then first either the
disposition is reset to SIG_DFL, or the signal is blocked (see
Portability below), and then handler is called with argument
signum. If invocation of the handler caused the signal to be
blocked, then the signal is unblocked upon return from the
handler.
> The signals SIGKILL and SIGSTOP cannot be caught or ignored.
handler 會有以下三種情況:
* `SIGIGN` : 被忽略
* `SIGDFL` : 依預設狀況處理
* `SIGKILL` 與 `SIGSTOP`: 不能被捕捉或忽略
:::info
不確定SIGDFL的意思,待查
:::
另外,參考 CSAPP3e ch8.5 的簡介,signal 是 linux 上對於例外控制流程 (exception control flow) 的實做,可讓 process 或 kernel 對目標的 process 產生中斷,並通知目標 process 目前的系統事件。
[signal(7)](http://man7.org/linux/man-pages/man7/signal.7.html) 列出了 linux 上支援的 signal 種類:
```
Signal Value Action Comment
──────────────────────────────────────────────────────────────────────
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating-point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no
readers; see pipe(7)
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process
The signals SIGKILL and SIGSTOP cannot be caught, blocked, or
ignored.
Next the signals not in the POSIX.1-1990 standard but described in
SUSv2 and POSIX.1-2001.
Signal Value Action Comment
────────────────────────────────────────────────────────────────────
SIGBUS 10,7,10 Core Bus error (bad memory access)
SIGPOLL Term Pollable event (Sys V).
Synonym for SIGIO
SIGPROF 27,27,29 Term Profiling timer expired
SIGSYS 12,31,12 Core Bad system call (SVr4);
see also seccomp(2)
SIGTRAP 5 Core Trace/breakpoint trap
SIGURG 16,23,21 Ign Urgent condition on socket (4.2BSD)
SIGVTALRM 26,26,28 Term Virtual alarm clock (4.2BSD)
SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2BSD);
see setrlimit(2)
SIGXFSZ 25,25,31 Core File size limit exceeded (4.2BSD);
see setrlimit(2)
```
應用案例:
在 homework 2 使用的自動化測試程式 qtest ,其中的 [queue_init()](https://github.com/wingard/lab0-c/blob/master/qtest.c#L488) 就有使用到 signal ,將 SIESEGV 與 SIGALRM 兩種 signal 中斷,對應到 sigsegvhandler() 以及 sigalrmhandler() 兩個 signal handler:
```C=1
static void queue_init()
{
fail_count = 0;
q = NULL;
signal(SIGSEGV, sigsegvhandler);
signal(SIGALRM, sigalrmhandler);
}
```
其對應的 signal interrupt handler ,函數宣告都是 `void SignalHandler (int sig)`
```C=1
/* Signal handlers */
void sigsegvhandler(int sig)
{
trigger_exception(
"Segmentation fault occurred. You dereferenced a NULL or invalid "
"pointer");
}
void sigalrmhandler(int sig)
{
trigger_exception(
"Time limit exceeded. Either you are in an infinite loop, or your "
"code is too inefficient");
}
```
---
## [第 2 週 測驗 4](https://hackmd.io/s/BJA6LjbK7)
考慮到以下程式碼:
```clike
int B = 2;
void func(int *p) { p = &B; }
int main() {
int A = 1, C = 3;
int *ptrA = &A;
func(ptrA);
printf("%d\n", *ptrA);
return 0;
}
```

該如何修改,才能讓 `func` 得以變更到 `main` 裡頭 `ptrA` 的內含值?
:::success
延伸問題: 在 GitHub 找出使用 the pointer to the pointer 的 C 語言程式碼,並加以討論
:::
---
使用指標的指標,來延伸原本function內變數的生命週期
* 由於變數 p增加了一個 * (dereference),其對應變數的生命週期 (lifetime) 就擴張到 func 的上一層使用範圍內
```C=1
// p will be kept
int B = 2;
void func(int **p) { *p = &B; }
int main() {
int A = 1, C = 3;
int *ptrA = &A;
func(&ptrA);
printf("%d\n", *ptrA);
return 0;
}
```
:::info
:::
---
## [第 3 週 測驗 1](https://hackmd.io/s/BkyQskjK7)
考慮以下程式碼:
```C=
#include <stdio.h>
#include <stdint.h>
struct test {
unsigned int x : 5;
unsigned int y : 5;
unsigned int z;
};
int main() {
struct test t;
printf("Offset of z in struct test is %ld\n",
(uintptr_t) &t.z - (uintptr_t) &t);
return 0;
}
```
在 GNU/Linux x86_64 環境中執行,得到以下輸出:
```
Offset of z in struct test is 4
```
倘若將第 10 和第 11 換為以下:
```C=10
printf("Address of t.x is %p", &t.x);
```
會發生什麼事?
==作答區==
* `(a)` 印出類似 `0x7ffd144e8ad4` 的輸出,也就是 `t` 結構物件的位址;
* `(b)` 印出類似 `0x7ffd144e8ad4` 的輸出,和 `t` 結構物件差距 4 bytes;
* `(c)` 可以印出數值,但這與編譯器實作有關,不能解讀;
* `(d)` 編譯失敗,不能這樣宣告結構體的成員;
* `(e)` ==編譯失敗,不能將指標指向沒有對齊 1 byte 的結構體成員;==
參考資料: [Portable Bit Fields in packetC](https://link.springer.com/content/pdf/10.1007/978-1-4302-4159-1_34.pdf)
:::success
延伸問題: 解釋原因,並且找出 C99/C11 規格書對應的描述
:::
---
在 C99 規格書搜尋 alignment,3.2 節有提到 alignment 的定義:
> **alignment**
requirement that objects of a particular type be located on storage boundaries with addresses that are particular multiples of a byte address
即,對齊必須以 byte address (8 bit) 為單位
繼續往下尋找,可以在 6.2.5 節找到跟 pointer 有關的敘述:
> A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.
也就是說,pointer to structure type 也必須要遵守 alignment 的原則
由此可得知,由於`&t.x` 會無法指向到一個未對齊的結構元素上,故會在編譯時期產生 `error: cannot take address of bit-field ‘x’` 的錯誤。
---
## [第 3 週 測驗 2](https://hackmd.io/s/BkyQskjK7)
考慮以下程式碼,在 little-endian 硬體上,會返回 `1`,反之,在 big-endian 硬體上會返回 `0`:
```C
int main() {
union { int a; char b;
} c = { .a = K1 };
return c.b == K2;
}
```
補完上述程式碼。
==作答區==
K1 = ?
* `(a)` 0
* `(b)` ==1==
* `(c)` -1
* `(d)` 254
K2 = ?
* `(a)` 0
* `(b)` ==1==
* `(c)` 254
:::success
延伸問題: 解釋運作原理,並找出類似的程式碼
:::
---
Union 與 Struct 不同的地方在於,Union 讓不同資料型態的變數共用同一個記憶體空間,且配置的記憶體空間為裡面佔用最大記憶體空間者 (以此題來說就是 int ): [Source](https://www.geeksforgeeks.org/difference-structure-union-c/)
但由於 endianness 的緣故:
如果是 Little endian ,因為低位元組先存在低位址,所以 char 與 int 都可讀到相同的 byte value 0x01 。
如果是 Big endian ,因為高位元組先存在低位址,所以 byte value 0x01 會存在高位址, char 會讀到 0x00 。
類似的程式碼:[Source](http://cs-fundamentals.com/tech-interview/c/c-program-to-check-little-and-big-endian-architecture.php)
```C=1
/*
Write a C program to find out if the underlying
architecture is little endian or big endian.
*/
#include <stdio.h>
int main ()
{
unsigned int x = 0x76543210;
char *c = (char*) &x;
printf ("*c is: 0x%x\n", *c);
if (*c == 0x10)
{
printf ("Underlying architecture is little endian. \n");
}
else
{
printf ("Underlying architecture is big endian. \n");
}
return 0;
}
```