# 2021q1 Homework1 (lab0)
contributed by < [`andy78644`](https://github.com/andy78644) >
###### tags: `2021 linux`
## 實驗環境資訊
```shell
$ uname -a
Linux andy78644-GP72VR-7RFX 5.4.0-66-generic #74-Ubuntu SMP Wed Jan 27 22:54:38 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
```
:::danger
注意共筆寫作規範:中英文間用一個空白字元區隔。
:notes: jserv
:::
## 開發過程
### `q_new`
* 分派一個 queue_t 的空間給 q,須確定是否有分派成功
* 進行初始化,將 head 與 tail 均指向 NULL ,以及 size 設為0
```clike=
queue_t *q_new()
{
queue_t *q;
if (!(q = malloc(sizeof(queue_t))))
return NULL;
q->head = NULL;
q->tail = NULL;
q->size = 0;
return q;
}
```
### `q_free`
* 如果 q 為空,沒有空間須釋放可直接返回
* 要先將字串空間釋放掉在釋放節點空間
```clike=
void q_free(queue_t *q)
{
if (!q)
return;
while (q->head) {
list_ele_t *tmp = q->head;
q->head = q->head->next;
free(tmp->value);
free(tmp);
q->size--;
}
free(q);
}
```
### `q_size`
* 為了達成 O(1) 因此可以在佇列的結構上加入 size ,在呼叫 q_size 時即可直接回傳 size ,不須在經過搜尋每個節點計算 size 大小
```clike=
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int size;
} queue_t;
int q_size(queue_t *q)
{
if (!q)
return 0;
return q->size;
}
```
### `q_insert_head`
* 須分別分配空間給節點與字串,並確定是否 malloc 成功
```clike=
bool q_insert_head(queue_t *q, char *s)
{
list_ele_t *newh;
size_t slen = strlen(s) + 1;
/* TODO: What should you do if the q is NULL? */
if (!q) {
return false;
}
if (!(newh = malloc(sizeof(list_ele_t))))
return false;
if (!(newh->value = malloc(slen))) {
free(newh->value);
free(newh);
return false;
}
/* Don't forget to allocate space for the string and copy it */
/* What if either call to malloc returns NULL? */
memcpy(newh->value, s, slen);
if (!q->head)
q->tail = newh;
newh->next = q->head;
q->head = newh;
q->size++;
return true;
}
```
### `q_insert_tail`
* 為了達成 O(1) ,須加入 tail 的指標指向串列的最後一個節點,在尾端插入節點時,只要直接使用 tail 的指標去作梗改不須重串列的第一個節點開始尋找
* 如果串列為空,須將 head 的指標指向新插入的節點
```clike=
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
int size;
} queue_t;
bool q_insert_tail(queue_t *q, char *s)
{
list_ele_t *newh;
if (!q)
return false;
size_t slen = strlen(s) + 1;
if (!(newh = malloc(sizeof(list_ele_t))))
return false;
if (!(newh->value = malloc(sizeof(char) * (slen + 1)))) {
free(newh->value);
free(newh);
return false;
}
memcpy(newh->value, s, slen);
newh->next = NULL;
if (q->head)
q->tail->next = newh;
else
q->head = newh;
q->tail = newh;
q->size++;
return true;
}
```
### `q_remove_head`
* 須先確定是否有 sp 的空間可以存取,可能不須回傳 sp
* 須確定長度不能超過 bufsize 會產生超出記憶體空間的問題
```clike=
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (!q || !(q->head))
return false;
list_ele_t *tmp = q->head;
q->head = tmp->next;
int len = strlen(tmp->value);
if (sp) {
memset(sp, 0, bufsize);
if (len < bufsize) {
memcpy(sp, tmp->value, len);
} else {
memcpy(sp, tmp->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
}
free(tmp->value);
free(tmp);
q->size--;
return true;
}
```
### `q_reverse`
```graphviz
digraph graphname{
node[shape=box]
head[shape=plaintext,fontcolor=red]
cur[shape=plaintext,fontcolor=red]
tmp[shape=plaintext,fontcolor=red]
NULL2[label=NULL,shape=plaintext]
rankdir=LR
{
rankdir = LR
A[label=A]
B[label=B]
C[label=C]
NULL1[label=NULL,shape=plaintext]
}
{
rank="same";
head->A
}
{
rank="same";
cur->B
}
{
rankdir=LR;
tmp->NULL2
}
A->B->C->NULL1
}
```
```graphviz
digraph graph2{
node[shape=box]
head[shape=plaintext,fontcolor=red]
cur[shape=plaintext,fontcolor=red]
tmp[shape=plaintext,fontcolor=red]
rankdir=LR
{
rankdir = LR
A[label=A]
B[label=B]
C[label=C]
NULL1[label=NULL,shape=plaintext]
}
{
rank="same";
head->B
}
{
rank="same";
cur->C
}
{
rank="same";
tmp->A
}
rankdir = LR
B->C->NULL1
A->B[style="dashed"]
}
```
```graphviz
digraph graph2{
node[shape=box]
head[shape=plaintext,fontcolor=red]
cur[shape=plaintext,fontcolor=red]
tmp[shape=plaintext,fontcolor=red]
rankdir=LR
{
rankdir = LR
A[label=A]
B[label=B]
C[label=C]
NULL1[label=NULL,shape=plaintext]
}
{
rank="same";
head->B
}
{
rank="same";
cur->C
}
{
rank="same";
tmp->A
}
B->C->NULL1
rankdir =RL
B->A
}
```
```clike=
void q_reverse(queue_t *q)
{
if (!q)
return;
list_ele_t *cur;
list_ele_t *tmp = NULL;
if (!(q->head))
return;
q->tail = q->head;
while (q->head) {
cur = q->head;
q->head = cur->next;
cur->next = tmp;
tmp = cur;
}
q->head = cur;
}
```
### `q_sort`
* 利用 mergesort 進行排序已達到 O(nlogn)
```clike=
static list_ele_t *merge(list_ele_t *, list_ele_t *);
static list_ele_t *mergesort(list_ele_t *);
void q_sort(queue_t *q)
{
if (!q)
return;
if (!(q->head) || !(q->head->next))
return;
q->head = mergesort(q->head);
list_ele_t *tmp = q->head;
while (tmp->next) {
tmp = tmp->next;
}
q->tail = tmp;
}
static list_ele_t *merge(list_ele_t *left, list_ele_t *right)
{
list_ele_t *tmp, *q;
int compare = 0;
if (right && left)
compare = strcmp(left->value, right->value);
if (compare < 0 || !(right)) {
tmp = left;
left = left->next;
} else {
tmp = right;
right = right->next;
}
q = tmp;
while (left && right) {
compare = strcmp(left->value, right->value);
if (compare < 0) {
tmp->next = left;
left = left->next;
tmp = tmp->next;
} else {
tmp->next = right;
right = right->next;
tmp = tmp->next;
}
}
if (!left) {
tmp->next = right;
} else {
tmp->next = left;
}
return q;
}
static list_ele_t *mergesort(list_ele_t *head)
{
if (!(head) || !(head->next)) {
return head;
}
list_ele_t *right = head->next;
list_ele_t *left = head;
while (right && right->next) {
left = left->next;
right = right->next->next;
}
right = left->next;
left->next = NULL;
head = mergesort(head);
right = mergesort(right);
return merge(head, right);
}
```
## Address Sanitizer
使用 `make SANITIZER=1` 進行編譯後,執行後輸入 `help` 產生下列錯誤
```shell=
==49502==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55fbea41f3c0 at pc 0x55fbea4087bd bp 0x7ffc1b66fc00 sp 0x7ffc1b66fbf0
READ of size 4 at 0x55fbea41f3c0 thread T0
#0 0x55fbea4087bc in do_help_cmd /home/e24064135/linux2021/lab0-c/console.c:307
#1 0x55fbea4088d0 in interpret_cmda /home/e24064135/linux2021/lab0-c/console.c:221
#2 0x55fbea4090b5 in interpret_cmd /home/e24064135/linux2021/lab0-c/console.c:244
#3 0x55fbea40a7f8 in run_console /home/e24064135/linux2021/lab0-c/console.c:660
#4 0x55fbea4073e5 in main /home/e24064135/linux2021/lab0-c/qtest.c:780
#5 0x7f59835b40b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#6 0x55fbea404b8d in _start (/home/e24064135/linux2021/lab0-c/qtest+0x8b8d)
0x55fbea41f3c1 is located 0 bytes to the right of global variable 'echo' defined in 'console.c:59:13' (0x55fbea41f3c0) of size 1
SUMMARY: AddressSanitizer: global-buffer-overflow /home/e24064135/linux2021/lab0-c/console.c:307 in do_help_cmd
```
在console.c內59行
```clike=
static bool echo = 0;
```
查詢307行可看到以下結果
```clike=
while (plist) {
report(1, "\t%s\t%d\t%s", plist->name, *plist->valp,plist->documentation);
plist = plist->next;
}
```
往前找尋 plist 宣告的型態為 param_ptr
```clike=
param_ptr plist = param_list;
```
在 console.c 中可找到 `echo` 在 add_param 以強轉型的方式放入,而 echo 本身為 bool 型態因此會產生 address 的問題
```clike=
add_param("echo", (int *) &echo, "Do/don't echo commands", NULL);
void add_param(char *name,
int *valp,
char *documentation,
setter_function setter)
{
param_ptr next_param = param_list;
param_ptr *last_loc = ¶m_list;
while (next_param && strcmp(name, next_param->name) > 0) {
last_loc = &next_param->next;
next_param = next_param->next;
}
param_ptr ele = (param_ptr) malloc_or_fail(sizeof(param_ele), "add_param");
ele->name = name;
ele->valp = valp;
ele->documentation = documentation;
ele->setter = setter;
ele->next = next_param;
*last_loc = ele;
}
```
將 echo 改為宣告 int 即可解決
```clike=
static int echo = 0;
```
在進行 `make test`中會產生令一項錯誤
```shell=
==45984==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5617dab125e0 at pc 0x5617daafaae8 bp 0x7ffdea15c900 sp 0x7ffdea15c8f0
READ of size 4 at 0x5617dab125e0 thread T0
#0 0x5617daafaae7 in do_option_cmd /home/e24064135/linux2021/lab0-c/console.c:369
#1 0x5617daaf98d0 in interpret_cmda /home/e24064135/linux2021/lab0-c/console.c:221
#2 0x5617daafa0b5 in interpret_cmd /home/e24064135/linux2021/lab0-c/console.c:244
#3 0x5617daafb2e1 in cmd_select /home/e24064135/linux2021/lab0-c/console.c:594
#4 0x5617daafb85b in run_console /home/e24064135/linux2021/lab0-c/console.c:667
#5 0x5617daaf83e5 in main /home/e24064135/linux2021/lab0-c/qtest.c:780
#6 0x7f03f6fb90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#7 0x5617daaf5b8d in _start (/home/e24064135/linux2021/lab0-c/qtest+0x8b8d)
0x5617dab125e1 is located 0 bytes to the right of global variable 'simulation' defined in 'console.c:21:6' (0x5617dab125e0) of size 1
'simulation' is ascii string ''
SUMMARY: AddressSanitizer: global-buffer-overflow /home/e24064135/linux2021/lab0-c/console.c:369 in do_option_cmd
```
與上面的 echo 相同,也是在 add_param 的參數中有強轉型的 simulation 為 bool 型態,因此產生錯誤
console.c
```clike=
int simulation = false;
```
console.h
```clike=
extern int simulation;
```