# Ćwiczenia 4, grupa cz. 10-12, 3 listopada 2022
###### tags: `SO22` `ćwiczenia` `pwit`
## Deklaracje
Gotowość rozwiązania zadania należy wyrazić poprzez postawienie X w odpowiedniej kolumnie! Jeśli pożądasz zreferować dane zadanie (co najwyżej jedno!) w trakcie dyskusji oznacz je znakiem ==X== na żółtym tle.
**UWAGA: Tabelkę wolno edytować tylko wtedy, gdy jest na zielonym tle!**
:::warning
Dzisiaj zadania 5-8 z listy 3 oraz 1-6 z listy 4.
Link do formularza z listą 3: [kliknij tu](https://hackmd.io/@iiuwr-kba/SkuBJ3PEo/edit)
:::
:::danger
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| ----------------------:| ----- | --- | --- | --- | --- | --- | --- | --- |
Miriam Bouhajeb | X | | | | X | | ==X== | |
Kacper Chmielewski | | | | | X | X | | X |
Jan Jankowicz | X | | | | X | | | |
Jakub Kaczmarek | | | | | | | | |
Jarosław Kadecki | X | X | | | X | X | | |
Zuzanna Kania | | | | | X | X | | X |
Julia Konefał | | | | | X | | | |
Daniel Sarniak | X | X | | | X | X | X | X |
Paweł Tkocz | X | | X | | X | X | X | X |
Miłosz Urbanik | | | | | | | | |
Tomasz Wołczański | X | X | | X | X | X | X | X |
Radosław Śliwiński | | | | | X | | | |
:::
:::info
**Uwaga:** Po rozwiązaniu zadania należy zmienić kolor nagłówka na zielony.
:::
## Zadanie 1
:::success
Autor: Tomasz Wołczański
:::


```c=
void tty_curpos(int fd, int *x, int *y) {
struct termios ts, ots;
tcgetattr(fd, &ts);
memcpy(&ots, &ts, sizeof(struct termios));
// disable ECHO to prevent the terminal from displaying anything after receiving CPR
// disable ICANON to be able to write CPR without the newline character
// disable CREAD to prevent reading input from the terminal device
ts.c_lflag &= ~(ECHO | ICANON | CREAD);
tcsetattr(fd, TCSADRAIN, &ts);
/* How many characters in the input queue. */
int m = 0;
/* TODO: Need to figure out some other way to do it on MacOS / FreeBSD. */
#ifdef LINUX
ioctl(fd, TIOCINQ, &m);
#endif
/* Read them all. */
char discarded[m];
m = Read(fd, discarded, m);
Write(fd, CPR(), sizeof(CPR()));
// read the cursor position output by the terminal
char buf[20];
int n = Read(fd, buf, 19);
buf[n] = '\0';
// enable ICANON to prevent each character from leaving the input queue after being put back
ts.c_lflag |= ICANON;
tcsetattr(fd, TCSADRAIN, &ts);
// send the discarded characters back to the input queue
for (int i = 0; i < m; i++)
ioctl(fd, TIOCSTI, discarded + i);
// restore old attributes
tcsetattr(fd, TCSADRAIN, &ots);
// store the cursor position in x and y
// the format of the terminal output is \033[n;mR
// where n is the row and m is the column
sscanf(buf, "\033[%d;%dR", x, y);
}
```
Kod CPR służy do odczytania pozycji kursora. Pozycja jest wypisywana przez terminal w formacie `\033[n;mR`, gdzie `n` to wiersz, a `m` to kolumna.
`ioctl(fd, TCGETS, argp)` - pobiera aktualne atrybuty terminala i zapisuje je do `argp`. Równoważne z `tcgetattr(fd, argp)`.
`ioctl(fd, TCSETSW, argp)` - ustawia atrybuty terminala na te zapisane w `argp` po przetworzeniu wszystkich znaków z kolejki wyjściowej. Równoważne z `tcsetattr(fd, TCSADRAIN, argp)`.
`ioctl(fd, TIOCINQ, argp)` - wpisuje liczbę bajtów (znaków) w kolejce wejściowej do `argp`.
`ioctl(fd, TIOCSTI, argp)` - dodaje znak `argp` do kolejki wejściowej.
## Zadanie 2
:::success
Autor: Jarosław Kadecki

:::
script -f -t timing.log 2>timing.timing -c dash
ls
scriptreplay timing.timing timing.log
sudo strace -f -e trace=read,write -o script.log scriptreplay timing.timing timing.log
icrnl- translate carriage return to newline
onlcr - translate newline to carriage return-newline
A pseudoterminal (sometimes abbreviated "pty") is a pair of virtual character devices that provide a bidirectional communication channel. One end of the channel is called the master; the other end is called the slave. The slave end of the pseudoterminal provides an interface that behaves exactly like a classical terminal. A process that expects to be connected to a terminal, can open the slave end of a pseudoterminal and then be driven by a program that has opened the master end. Anything that is written on the master end is provided to the process on the slave end as though it was input typed on a terminal.
script.log

## Zadanie 3
:::success
Autor: Paweł Tkocz

System z tworzeniem rur:
Rodzic wywołuje pipe() i dostaje dwa deskryptory plików pomiędzy któymi została utworzona rura. Oznaczmy je jako rd i wr
Rodzic wykonuje forka. Ponieważ deskryptory plików są kopiowane do dziecka, i rodzic i dziecko mają rd i wr
Załóżmy, że dziecko jest nastawione na czytanie. Wtedy
Rodzic powinien zamknąć koniec rury do czytania
Dziecko powinno zamknąć koniec rury do pisania

Schemat najważniejszych wywołań systemowych kluczowych dla utworzenia rur:
(Proces programu dash to 6530)
6530 tworzy pipe(3, 4a) i wykonujemy clone (nowy PID = 6538)
6530 close(4a), pipe(4b, 5) i clone (nowy PID = 6539)
6530 close(3), close(5) i clone (nowy PID = 6540)
6538 close(3)
6538 dup2(4a, 1)
6530 close(4b)
6538 close(4a)
6538 execve (ps z argumentem -ef)
6539 close(4b)
6539 dup2(3, 0)
6539 close(3)
6539 dup2(5, 1)
6540 dup2(4, 0)
6539 close(5)
6540 close(4b)
6539 execve (grep z argumentem sh)
6540 openat(AT_FDCWD, "cnt", O_WRONLY|O_CREAT|O_TRUNC, 0666) (fd 3= cnt)
6540 close(1)
6540 dup2(3, 1)
6540 execve (wc z argumentem -l)
6538 close(1), close(2)
6539 close(1), close(2)
6540 close(0), close(1), close(2)
Ostatecznie
6538(ps) STDOUT: 4a
6539(grep) STDIN: 3 STDOUT: 5
6540(wc) STDIN: 4b STDOU: 3(cnt)
A program utworzył dwa pipe:
1. pipe(3, 4a)
2. pipe(4b, 5)
Oraz przekierowanie
STDOUT procesu 6540(wc) wskazuje na fd 3 czyli plik cnt
:::
## Zadanie 4
:::success
Autor: Tomasz Wołczański
:::

Powłoka tworzy nową grupę procesów po utworzeniu podprocesu odpowiadającemu pierwszemu programowi z potoku; podproces ten staje się liderem grupy.
```
25494 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0d3d8d2a10) = 25495
25494 setpgid(25495, 25495) = 0
```
Pozostałe procesy są dodawane do grupy w taki sam sposób, w jaki został do niej dodany pierwszy proces, czyli wywołaniem systemowym `setpgid`:
```
25494 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0d3d8d2a10) = 25496
25494 setpgid(25496, 25495 <unfinished ...>
```
```
25494 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD <unfinished ...>
25494 setpgid(25497, 25495 <unfinished ...>
```
`setpgid` jest wywoływane zarówno przez powłokę, jak i procesy realizujące potok:
```
25494 setpgid(25495, 25495) = 0
25495 setpgid(0, 25495) = 0
```
```
25494 setpgid(25496, 25495 <unfinished ...>
25496 setpgid(0, 25495 <unfinished ...>
```
```
25494 setpgid(25497, 25495 <unfinished ...>
25497 setpgid(0, 25495 <unfinished ...>
```

Zarówno gdyby sama powłoka wywoływała `setpgid`, jak i gdyby sam proces potomny wywoływał `setpgid` mielibyśmy *race condition* wynikające z niedeterministycznego przeplotu procesów po wywołaniu `fork`. W pierwszym przypadku powłoka mogłaby wywołać `setpgid` dopiero po wywołaniu `exec` przez proces potomny, a próba zmiany identyfikatora grupy dziecka po tym, jak wywołało ono `exec` kończy się błędem `EACCES`. W drugim przypadku powłoka mogłaby wysłać jakiś sygnał do grupy zanim proces potomny zdążyłby się do tej grupy dopisać.
Po utworzeniu proces powłoki ustala grupę pierwszoplanową na swoją własną grupę, a po zakończeniu działania przywraca na pierwszy plan poprzednią grupę:
```
25494 setpgid(0, 25494) = 0
25494 ioctl(10, TIOCSPGRP, [25494]) = 0
```
```
25494 ioctl(10, TIOCSPGRP, [25491]) = 0
```
Dodatkowo, procesy realizujące potok ustanawiają grupą pierwszoplanową ich własną grupę:
```
25495 setpgid(0, 25495) = 0
25495 ioctl(10, TIOCSPGRP, [25495]) = 0
```
```
25496 setpgid(0, 25495 <unfinished ...>
25496 ioctl(10, TIOCSPGRP, [25495]) = 0
```
```
25497 setpgid(0, 25495 <unfinished ...>
25497 ioctl(10, TIOCSPGRP, [25495]) = 0
```
Kodem wyjścia potoku jest kod wyjścia jego ostatniego polecenia, czyli w przypadku `ps -ef | grep sh | wc -l > cnt` kodem wyjścia będzie kod wyjścia procesu realizującego `wc -l > cnt`.
## Zadanie 5
:::success
Autor: Radosław Śliwiński

Procedury `read()` i `write()` nie moga czytac katalogow, poniewaz wymagaja one file descriptora, a katalogi sa specjalnym typem pliku, ktory nie udostepnia swojego `fd`.
Aby odczytac zawartosc katalogu nalezy uzyc `readdir()` lub `opendir()`
Zawartosc katalogu nie jest posortowana poniewaz, gdy kernel tworzy nowy plik w katalogu wypelni pierwsze wolne miejsce w pliku katalogu, przez co posortowanie katalogu nie jest gwarantowane.
Wywolanie `stat /`:

Wynika z tego, że katalog `/` zawiera 17 `twardych dowiązań` (ang. hard links), które tworzą nową nazwę dla danego zasobu i zapisują ją w nowej lokalizacji (nie kasując poprzedniej), a same dowiązania nie odwołują się do samego katalogu, lecz do jego zawartości. Inną definicją dowiązania twardego jest liczba wskaźników na inode’y plików, które wliczają się do licznika referencji do pliku.
:::
## Zadanie 6
:::success
Autor: Kacper Chmielewski

Time of Check To Time of Use (TOCTTOU) jest błędem polegającym na tym, że atakujący może zmienić dane w momencie kiedy program sprawdza dane, a zanim wykona operacje.
```c
bool f_lock(const char *path) {
return Open(path, O_CREAT | O_EXCL | O_WRONLY, 0700) >= 0;
}
```
:::
## Zadanie 7
:::success
Autor: Daniel Sarniak

Dowiązanie symboliczne - Wskazuje odwołując się za pomocą nazwy, na dowolny inny plik lub katalog
innocent.c
```c=
int main(void) {
long max_fd = sysconf(_SC_OPEN_MAX);
int out = Open("/tmp/hacker", O_CREAT | O_APPEND | O_WRONLY, 0666);
/* TODO: Something is missing here! */
const int buf_size = 2048;
char link[buf_size];
char path[buf_size];
for (int i = 0; i < max_fd; i++) {
/*lseek(fd,0,0) zmiana aktualnej pozycji w pliku na - fd, 0 - nowa pozycja w pliku, 0 - pozcyja bezwezgledna*/
if (lseek(i, 0, 0) >= 0) {
snprintf(link, buf_size, "/proc/%d/fd/%d", getpid(), i);
int p_lenght;
/*readlink umieszcza zawartość dowiązania symbolicznego link w buforze path, którego wielkość
*wynosi buf_size.*/
if ((p_lenght = Readlink(link, path, buf_size)) < 0) {
fprintf(stderr, "Readlink error");
exit(1);
}
path[p_lenght] = '\0';
dprintf(out, "File descriptor %d is '%s' file!\n", i, path);
fprintf(stdout, "File descriptor %d is '%s' file!\n", i, path);
int r_cnt;
char buf[2048];
int cur_off = Lseek(i, 0, SEEK_CUR);
while ((r_cnt = read(i, buf, sizeof(buf))) > 0) {
Write(out, buf, r_cnt);
}
Lseek(i, cur_off, SEEK_SET);
}
}
Close(out);
printf("I'm just a normal executable you use on daily basis!\n");
return 0;
}
```
leaky.c
```c=
int main(int argc, char **argv) {
long max_fd = sysconf(_SC_OPEN_MAX);
/* Initialize PRNG seed. */
struct timeval tv;
gettimeofday(&tv, NULL);
srandom(tv.tv_usec);
/* This opens a file with password that is checked later. */
int fd_1 = Open("mypasswd", O_RDONLY, 0);
int fd_2 = 3 + random() % (max_fd - 3);
(void)Dup2(fd_1, fd_2);
Close(fd_1);
Lseek(fd_2, 0, SEEK_END);
/* TODO: Something is missing here to fix the issue! */
/*
* fcntl - manipulate file descriptor
* F_SETFD - Set the file descriptor flags to the value specified by arg
* FD_CLOEXEC - If the FD_CLOEXEC bit is set, the file descriptor will automatically be closed during a successful execve(2)
*/
fcntl(fd_2, F_SETFD, FD_CLOEXEC);
/* Let's suppose a user typed in correct password and was allowed to execute
* a command and they choose to run our program. */
int rc = system("./innocent");
if (rc < 0)
unix_error("System error");
/* At this point we may finally close the file. */
Close(fd_2);
return rc;
}
```
:::
## Zadanie 8
:::success
Autor: Kacper Chmielewski

```
> stat holes.bin
File: holes.bin
Size: 33550336 Blocks: 1112 IO Block: 4096 regular file
Device: 259,5 Inode: 3581933 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ kacper) Gid: ( 1000/ kacper)
Access: 2022-11-16 21:38:09.928736350 +0100
Modify: 2022-11-16 21:38:09.942069021 +0100
Change: 2022-11-16 21:38:09.942069021 +0100
Birth: 2022-11-16 21:38:09.928736350 +0100
```
```
The stat structure
All of these system calls return a stat structure, which contains
the following fields:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
```
```
st_blocks
This field indicates the number of blocks allocated to the
file, in 512-byte units. (This may be smaller than
st_size/512 when the file has holes.)
```
st_size / 512 = 65 528
Read and write
operations normally start at the current file offset and cause the offset to be incremented
by the number of bytes read or written.
:::