# 台大 108 計系 - UID, fork
###### tags: `資工所 - OS`


## 7a
### 解釋
當 process 執行時,會紀錄執行他的 user 的 user id 。其中 `real UID` 會紀錄該 user 真實的 User id ,對於非 root 而言無法更動。而 effective UID 則是 OS 在確認該 process 是否有權限執行操作時檢查的 ID , `effective UID` 是可以更動的,對於 root 而言(這邊指的是 `euid` = 0),可以更動為任意 uid ;對於非 root 而言更動的範圍為 `real UID` 以及 `saved UID` 。而 `saved UID` 預設為該 program 的 owner 。
`Effective UID` 的應用場景如下:
`Root` 開發了一支程式給 user 使用,其中有些操作需要 root 權限,例如需要寫入某個 owner 為 root 的 file 。為了讓 user 能在不具有 root 權限下使用該程式, root 可以將該程式的 set uid bit 設為 1 ,因此該程式在載入時的 `euid` 會為該程式的 own ,也就是 root 。因此該程式在運行時,就可以執行需要 root 權限才能執行的操作,又不會開放完整的 root 權限給 user (因為root 權限被限制在該程式中)。
### 實驗
```c=
#include <stdio.h>
#include <unistd.h>
void print_uid() {
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
}
int main(void) {
print_uid();
FILE *f = fopen("./test.txt", "r");
char buf[100];
if (!f) {
printf("Failed to open file\n");
return -1;
}
fscanf(f, "%s", buf);
printf("%s\n", buf);
fclose(f);
return 0;
}
```
其中 `test.txt` 只有 root 具有 write 的權限。
```
-rw------- 1 root root 12 Dec 24 06:14 test.txt
```
其內容為:
```shell
$ sudo cat test.txt
Hello world
```
我們為上面的程式編譯兩個版本,`main_root`, `main_user`:
```
all: main_user main_root
main_user: main.o
gcc -o main_user main.o
chmod +s ./main_user
main_root: main.o
sudo gcc -o main_root main.o
sudo chmod +s ./main_root
main.o: main.c
gcc -c main.c
clean:
rm -f main_* *.o
```
其中 `chmod +s` 為為該檔案加上 set uid bit ,也就是當程式載入時 `euid` 會被設為 `saved uid` 。
```shell
$ ll main_*
-rwsr-sr-x 1 root root 17056 Dec 24 07:10 main_root*
-rwsrwsr-x 1 jup9020 jup9020 17056 Dec 24 07:10 main_user*
```
可以發現權限的欄位中,原本為 `x` (executable) 的地方變成了 `s` ,這代表該程式的 set uid bit 為 1 。
如果我們使用 user 來執行 `main_user` ,會得到:
```shell
$ whoami
jup9020
$ ./main_user
UID: 1000
EUID: 1000
Failed to open file
```
毫不意外的,因爲沒有 `test.txt` 的 read 權限,因此 `fopen` 失敗。
若是我們使用 user 來執行 `main_root` 呢?
```shell
$ whoami
jup9020
$ ./main_root
UID: 1000
EUID: 0
Hello
```
可以發現,雖然我們使用 user 權限執行, `UID`(`real UID`) 不為 0(root) ,但是 `effective UID` 為 0(root) 。由於 OS 在檢查時是基於 `euid` 來決定是否具有權限,因此此程式具有權限能 open `test.txt` 並成功讀出。
---
若我們使用 root 來執行 `main_user` 呢?
```shell
# whoami
root
# ./main_user
UID: 0
EUID: 1000
Failed to open file
```
雖然我們使用 root 來執行 `main_user` ,但是由於該程式有啟用 set uid bit ,因此載入時的 `euid` 為 user(uid != 0) ,因此 OS 判定該程式不具有對 `test.txt` read 的權限。
### 解答
- 1
real uid - Load/execute 該 process 的 user 的 id
effective uid - 用於給 OS 檢查權限的 ID
saved uid - 當該 program 有設定 set uid bit 時, saved uid 會被設為 program owner 的 uid ,否則為 read uid 。 effective uid 可以利用 seteid 被更改為 saved uid 。
- 2
Effective UID ,因為 OS 是透過 euid 來確認權限。
## 7b
- 1
- memory
在 multiprocess 中,每個 process 具有獨立的記憶體空間,因此記憶體的操作並不會造成跨 process 的影響。
在 multithread 中, thread 間共享除了 stack 外的幾乎所有 segment ,因此記憶體操作時需要注意跨 thread 的影響。
- CPU scheduling
在 multiprocess 中, process 的 scheduling 是由 OS 來進行,以 OS 的角度而言,每個 process 皆為一個 kernel thread 。
在 multithread 中, thread 間的 scheduling 視 thread module 而定,若是 many to one 或 many to many 架構,則需要額外的 user level scheduler 來對 user thread 進行排程。
- 2
No, sharedVariable 是存在 stack 上,因為不同 process 的 stack 是彼此獨立的,因此在 child process 的改動並不會影響到 parent process 。
- 3
Yes, 在 fork 時,記憶體空間會被複製一份,`ptrSharedVariable` 內的值也會被複製,因此 16 , 23 行所印出的值是相同的。
- 4
題目有誤,根據 [man vfork](https://www.man7.org/linux/man-pages/man2/vfork.2.html)
:::info
Standard description
(From POSIX.1) The vfork() function has the same effect as
fork(2), ==except that the behavior is undefined if the process
created by vfork() either modifies any data other than a variable
of type pid_t used to store the return value from vfork(), or
returns from the function in which vfork() was called, or calls
any other function before successfully calling _exit(2) or one of
the exec(3) family of functions.==
Linux description
...
vfork() differs from fork(2) in that the calling thread is suspended until the child terminates (either nor‐
mally, by calling _exit(2), or abnormally, after delivery of a fatal signal), or it makes a call to ex‐
ecve(2). Until that point, the child shares all memory with its parent, including the stack. ==The child must
not return from the current function or call exit(3) (which would have the effect of calling exit handlers
established by the parent process and flushing the parent's stdio(3) buffers)==, but may call _exit(2).
:::
在 `vfork` 出的 child process 中改動資料或是 return 為未定義行為。
測試如下:
```c=
// main.c
#include <stdio.h>
#include <unistd.h>
int main(void) {
int a = 10;
pid_t pid = vfork();
if (pid == 0) {
a = 20;
} else {
printf("Parent: %d\n", a);
}
return 0;
}
```
```shell
$ for i in {0..10};
> do
> ./main
> done
Parent: 41450464
Segmentation fault (core dumped)
Parent: -648291360
Segmentation fault (core dumped)
Parent: -1480692768
Segmentation fault (core dumped)
Parent: -1464882208
Segmentation fault (core dumped)
Parent: 178805728
Segmentation fault (core dumped)
Parent: 107019232
Segmentation fault (core dumped)
Parent: -96281632
Segmentation fault (core dumped)
Parent: -1329452064
Segmentation fault (core dumped)
Parent: 1938688992
Segmentation fault (core dumped)
Parent: -1131680800
Segmentation fault (core dumped)
Parent: 1149926368
Segmentation fault (core dumped)
```
猜測出題教授希望以下答案:
C, 因爲使用 vfork 時,記憶體並不會被複製,因此 child process 對 stack 的改動會影響到 parent process 。