owned this note
owned this note
Published
Linked with GitHub
# 2021q1 Homework1 (lab0)
contributed by < `ilkclord` >
## Schedule
* [x] struct of queue
* [x] q_new
* [x] q_free
* [x] q_insert_head
* [x] q_insert_tail
* [x] q_remove_head
* [x] q_size
* [x] q_reverse
* [x] q_sort
* [x] console.h
* [x] qtest_debugging
* [ ] qtest-completing
## Development Detail
### queue.h
* **q_struct**
* ***head** the start of the linked list
* ***tail** use to make $O(1)$ time in insert_tail
```clike=
typedef struct {
list_ele_t *head;
list_ele_t *tail;
int size;
} queue_t;
```
### queue.c
* **q_new**
* create a new queue
* initialize the queue
``` clike=
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q == NULL)
return q;
q->head = NULL;
q->tail = q->head;
q->size = 0;
return q;
}
```
* **q_free**
* ***tmp** use for rember the address of next node
* free the string first then free the node
```clike=
void q_free(queue_t *q)
{
if (q != NULL) {
while (q->head != NULL) {
list_ele_t *tmp = q->head->next;
free(q->head->value);
free(q->head);
q->head = tmp;
}
free(q);
}
}
```
* **q_insert_head**
* Three checking gate to prevent acting on NULL ,empty and lack of memory
* Contents a special case for the inserting when th queue is empty
```clike=
bool q_insert_head(queue_t *q, char *s)
{
if (q == NULL)
return false;
list_ele_t *newh = malloc(sizeof(list_ele_t));
if (newh == NULL) {
free(newh);
return false;
}
char *tmp = malloc(strlen(s) + 1);
if (tmp == NULL) {
free(tmp);
free(newh);
return false;
}
strncpy(tmp, s, strlen(s));
tmp[strlen(s)] = '\0';
if (q->head == NULL) // special case
q->tail = newh;
newh->value = tmp;
newh->next = q->head;
q->head = newh;
q->size = q->size + 1;
return true;
}
```
* **q_insert_tail**
* Three checking gate to prevent acting on NULL ,empty and lack of memory
* Contents a special case for the inserting when queue is empty
```clike=
bool q_insert_tail(queue_t *q, char *s)
{
if (q == NULL)
return false;
list_ele_t *newt = malloc(sizeof(list_ele_t));
if (newt == NULL) {
free(newt);
return false;
}
char *tmp = malloc(strlen(s) + 1);
if (tmp == NULL) {
free(tmp);
free(newt);
return false;
}
strncpy(tmp, s, strlen(s));
tmp[strlen(s)] = '\0';
newt->next = NULL;
newt->value = tmp;
if (q->tail == NULL) { // special case
q->tail = newt;
q->head = newt;
return true;
}
q->tail->next = newt;
q->tail = newt;
q->size = q->size + 1;
return true;
}
```
* **q_remove_head**
* A checking gate for NULL and empty queue
* A checking gate for NULL sp
* free the string first then the node
```clike=
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (q == NULL || q->head == NULL)
return false;
if (sp != NULL) {
strncpy(sp, q->head->value, bufsize - 1);
sp[bufsize - 1] = '\0';
}
list_ele_t *tmp = q->head->next;
free(q->head->value);
free(q->head);
q->head = tmp;
q->size = q->size - 1;
return true;
}
```
* **q_size**
* Operate in $O(1)$ time
* A checking gate for NULL and empty queue
```clike=
int q_size(queue_t *q)
{
if (q == NULL || q->head == NULL)
return 0;
return q->size;
}
```
* **q_reverse**
* First check if the queue has at least 2 elements
* Change the direction between each elements
* Break the loop if reach the end (NULL)
```clike=
void q_reverse(queue_t *q)
{
if (q != NULL && q->head != NULL && q->head->next != NULL) {
q->tail = q->head;
list_ele_t *tmp = q->head->next;
list_ele_t *tmp2 = NULL;
q->head->next = NULL;
while (tmp != NULL) {
tmp2 = tmp->next;
tmp->next = q->head;
q->head = tmp;
tmp = tmp2;
}
}
}
```
* **q_sort**
* Add three function to complete the Task
```clike=
void q_sort(queue_t *q)
{
if (q == NULL || q->head == NULL || q->head->next == NULL)
return;
q->head = merge_split(q->head, q->size);
q->tail = merge_find(q->size, q->head);
}
```
* **merge**
* The merging process
* **q1** and **q2** represents two linked list
* Can merge two linked list with diffrent size
* Return a **merged** linked list
* First if_else determine the head of **merged** linked list
```clike=
list_ele_t *merge(list_ele_t *q1, list_ele_t *q2)
{
list_ele_t *leads = NULL, *root;
if (strcmp(q1->value, q2->value) < 0) {
leads = q1;
q1 = q1->next;
} else {
leads = q2;
q2 = q2->next;
}
root = leads;
while (q1 != NULL && q2 != NULL) {
if (strcmp(q1->value, q2->value) < 0) {
leads->next = q1;
q1 = q1->next;
} else {
leads->next = q2;
q2 = q2->next;
}
leads = leads->next;
}
if (q1 == NULL)
leads->next = q2;
else
leads->next = q1;
return root;
}
```
* **merge_find**
* Find the node in specific position
```clike=
list_ele_t *merge_find(int a, list_ele_t *root)
{
while (a > 1) {
root = root->next;
a = a - 1;
}
return root;
}
```
* **merge_split**
* First split the linked list to two linkend list
* The tail points to the NULL
* Do the merge-sort to the two linked list
* Recursively , return root if it has only ine elements
* Finally merge the two linked list to one
* Return the **merged-sorted** linked list
```clike=
list_ele_t *merge_split(list_ele_t *root, int size)
{
if (size / 2 > 0) {
list_ele_t *tmp = merge_find(size / 2, root);
list_ele_t *q2 = tmp->next;
tmp->next = NULL;
root = merge_split(root, size / 2);
q2 = merge_split(q2, size - size / 2);
return merge(root, q2);
} else {
root->next = NULL;
return root;
}
}
```
## Some Error
When making the test score 100/100 , there are some condition that i didn't notice
* **NULL States**
In the function below , I called the data in the nodes but the nodes are NULL actually.
* **q_insert_head**
The first insertion needs to initialize the tail too , so a gate is added
```clike=
if (q->head == NULL) // special case
q->tail = newh;
```
* **q_insert_tail**
The first insertion will failed because the tail is NULL ,and can't be called , so the gate below is added.
```clike=
if (q->tail == NULL) { // special case
q->tail = newt;
q->head = newt;
return true;
}
```
* **q_sort**
When finished sorting ,because the return linked list were copy to head, but tail has to modified too.
```clike=
q->tail = merge_find(q->size, q->head);
```
## Error in qtest
### Variable echo
By the command
```
$ make clean
$ make SANITIZER=1
$ ./qtest
$ cmd>help
```
We get the error message below
```clike
==11584==ERROR: AddressSanitizer: global-buffer-overflow on address 0x555e3e8143c0 at pc 0x555e3e7fd7bd bp 0x7ffce26b18b0 sp 0x7ffce26b18a0
READ of size 4 at 0x555e3e8143c0 thread T0
#0 0x555e3e7fd7bc in do_help_cmd /home/ilkclord/linux2020/lab0-c/console.c:307
#1 0x555e3e7fd8d0 in interpret_cmda /home/ilkclord/linux2020/lab0-c/console.c:221
#2 0x555e3e7fe0b5 in interpret_cmd /home/ilkclord/linux2020/lab0-c/console.c:244
#3 0x555e3e7ff7f8 in run_console /home/ilkclord/linux2020/lab0-c/console.c:660
#4 0x555e3e7fc3e5 in main /home/ilkclord/linux2020/lab0-c/qtest.c:780
#5 0x7fc192aea0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#6 0x555e3e7f9b8d in _start (/home/ilkclord/linux2020/lab0-c/qtest+0x8b8d)
0x555e3e8143c1 is located 0 bytes to the right of global variable 'echo' defined in 'console.c:59:13' (0x555e3e8143c0) of size 1
SUMMARY: AddressSanitizer: global-buffer-overflow /home/ilkclord/linux2020/lab0-c/console.c:307 in do_help_cmd
```
From the error message below we can figure out that
* The error occur in global variable **echo**
* It happend because reading size 4 in a size 1 memory
* The **echo** is a 1 byte variable
```clike
==11584==ERROR: AddressSanitizer: global-buffer-overflow on address 0x555e3e8143c0 at pc 0x555e3e7fd7bd bp 0x7ffce26b18b0 sp 0x7ffce26b18a0
READ of size 4 at 0x555e3e8143c0 thread T0
```
```clike
0x555e3e8143c1 is located 0 bytes to the right of global variable 'echo' defined in 'console.c:59:13' (0x555e3e8143c0) of size 1
```
Find the definition of **echo** in console.c
```clike
static bool echo = 0;
```
Find out the line first error occured
```clike
report(1, "\t%s\t%d\t%s", plist->name, *plist->valp,
plist->documentation);
```
We can know there are somthing wrong in 3 parameters , and they all belongs to **plist** which the type is **param_ptr**
```clike
param_ptr plist = param_list;
```
And the sturct of **param_ptr** can be find in console.h
```clike
typedef struct PELE param_ele, *param_ptr;
struct PELE {
char *name;
int *valp;
char *documentation;
/* Function that gets called whenever parameter changes */
setter_function setter;
param_ptr next;
};
```
Because the size of type **char** is 1 , the only possibe parameters which cause error is **int** ***valp**
TSo we find out that the **echo** is used to initialize **valp**
```clike
add_param("echo", (int *) &echo, "Do/don't echo commands", NULL);
```
And **add_param()** add a new node with type **param_ptr**
```clike
// part of the function
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;
```
Apparently **(int\*)&echo** is a invalid use ,because the size doesn't match (1 -> 4) .
To fix it ,we can just let the type of **echo** be **int** ,because there aren't any assinging true or false to **echo** action.
After applying the change , we fix it !
### Variable simulation
By the command
```clike
$ make clean
$ make SANITIZER=1
$ make test
```
We get the error message
```clike
==13139==ERROR: AddressSanitizer: global-buffer-overflow on address 0x564b115bb5e0 at pc 0x564b115a3ae8 bp 0x7ffe28ebd340 sp 0x7ffe28ebd330
READ of size 4 at 0x564b115bb5e0 thread T0
#0 0x564b115a3ae7 in do_option_cmd /home/ilkclord/linux2020/lab0-c/console.c:369
#1 0x564b115a28d0 in interpret_cmda /home/ilkclord/linux2020/lab0-c/console.c:221
#2 0x564b115a30b5 in interpret_cmd /home/ilkclord/linux2020/lab0-c/console.c:244
#3 0x564b115a42e1 in cmd_select /home/ilkclord/linux2020/lab0-c/console.c:594
#4 0x564b115a485b in run_console /home/ilkclord/linux2020/lab0-c/console.c:667
#5 0x564b115a13e5 in main /home/ilkclord/linux2020/lab0-c/qtest.c:780
#6 0x7f1b857f80b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#7 0x564b1159eb8d in _start (/home/ilkclord/linux2020/lab0-c/qtest+0x8b8d)
0x564b115bb5e1 is located 0 bytes to the right of global variable 'simulation' defined in 'console.c:21:6' (0x564b115bb5e0) of size 1
'simulation' is ascii string ''
SUMMARY: AddressSanitizer: global-buffer-overflow /home/ilkclord/linux2020/lab0-c/console.c:369 in do_option_cmd
```
From the error message we find the similar information of **simulation** as **echo**
* The error occur in global variable **simulation**
* It happend because reading size 4 in a size 1 memory
* The **simulation** is a 1 byte variable
```clike
==13139==ERROR: AddressSanitizer: global-buffer-overflow on address 0x564b115bb5e0 at pc 0x564b115a3ae8 bp 0x7ffe28ebd340 sp 0x7ffe28ebd330
READ of size 4 at 0x564b115bb5e0 thread T0
```
```clike
0x564b115bb5e1 is located 0 bytes to the right of global variable 'simulation' defined in 'console.c:21:6' (0x564b115bb5e0) of size 1
'simulation' is ascii string ''
```
We cna find out where **simulation** is used
```clike
add_param("simulation",(int*)&simulation, "Start/Stop simulation mode", NULL);
```
And since the definition shows that **simulation** is **bool** type , it is a inavalid tranfer .
```clike=
bool simulation = false;
```
We fixed it by changing the declared type to int, but then the error occured
```clike
ilkclord~01:36:38>~/linux2020/lab0-c>$ make SANTILIZER=1
CC qtest.o
CC report.o
CC console.o
console.c:21:5: error: conflicting types for ‘simulation’
21 | int simulation = false;
| ^~~~~~~~~~
In file included from console.c:3:
console.h:11:13: note: previous declaration of ‘simulation’ was here
11 | extern bool simulation;
| ^~~~~~~~~~
make: *** [Makefile:50: console.o] Error 1
```
So we go to console.h ,and change the type **bool** to **int** , then we fixed it !
```clike
extern int simulation;
```