---
# System prepended metadata

title: Linux-Project1
tags: [linux]

---

# Linux-Project1

###### tags: `linux`

> 第9組 
> 111522030 李孟潔
> 111522061 鄭伊涵
> 111526003 張友安

## Outline
[ToC]

## Project1
- https://staff.csie.ncu.edu.tw/hsufh/COURSES/FALL2022/linux_project_1.html

## Enviroment
* VMware
* Ubuntu 16.04 Lts
* Linux Kernel 4.15.1

## Kernel 編譯過程
### 遇到的問題
**1. memory空間不足**
>解決方法: 重割VM
   
**2. 使用oldconfig會出現error**
```c=
No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'
```
>修改CONFIG_SYSTEM_TRUSTED_KEYS，將其值改為空值
   
>修改CONFIG_SYSTEM_REVOCATION_KEYS，將其值改為空值


```c=
BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
```
>sudo apt install dwarves

修改完後編譯仍無法順利完成，後來改用menuconfig
   
**3. Kernel code版本相容性問題**
>一開始使用uname -r確認kernel版本為: 4.15.0-142-generic
>認為Kernel source code用4.15即可，但編譯過後仍有問題，最後使用4.15.1的版本才可以順利編譯過!
>向助教確認過，任何kernel版本都能向上支援!



## Kernel Space

### Code
```c=
#include <linux/syscalls.h>
#include <linux/printk.h>
#include <linux/mm_types.h>
#include <linux/slab.h>

SYSCALL_DEFINE3(get_physical_address, unsigned long*, initial, unsigned long*, result, int, n)
{
        pgd_t* pgd;
        pud_t* pud;
        pmd_t* pmd;
        pte_t* pte;

        unsigned long page_addr = 0;
        unsigned long page_offset = 0;
        unsigned long* va;
        unsigned long* pa;
        int i;
        long ret = 0;
        va = (unsigned long*)kmalloc(sizeof(unsigned long)*n,GFP_KERNEL);
        pa = (unsigned long*)kmalloc(sizeof(unsigned long)*n,GFP_KERNEL);
        ret = copy_from_user(va, initial, sizeof(*va)*n);
        for (i = 0; i < n; i++)
        {
                pgd = pgd_offset(current->mm, va[i]);
                pud = pud_offset((p4d_t*)pgd, va[i]);
                pmd = pmd_offset(pud, va[i]);
                pte = pte_offset_kernel(pmd, va[i]);

                page_addr = pte_val(*pte) & PAGE_MASK;
                page_offset = va[i] & ~PAGE_MASK;
                pa[i] = page_addr | page_offset;
        }
        ret = copy_to_user(result, pa, sizeof(*pa)*n);
        kfree(va);
        kfree(pa);
        return ret;

}

```

### Process
* **Virtual address 轉換成 Physical address** 
![](https://i.imgur.com/FGtABhz.png)

>64bits的linux kernel架構使用了4層的page table來做位址的轉換，分別為:
>-- Page Global Directory (PGD)
>-- Page Upper Directory (PUD)
>-- Page Middle Directory (PMD)
>-- Page Table (PTE)

>每一級table有3個key description marco: shift, size, mask。

>PGD, PUD, PMD, PTE會分別由pgd_t, pud_t, pmd_t, pte_t來做描述。

### SYSCALL_DEFINE
```c=
SYSCALL_DEFINE3(get_physical_address, unsigned long*, initial, unsigned long*, result, int, n)
```
#### Overview of the CVE-2009-0029
> The ABI in the Linux kernel 2.6.28 and earlier on s390, powerpc, sparc64, 
> and mips 64-bit platforms requires that a 32-bit argument in a 64-bit 
> register was properly sign extended when sent from a user-mode application, 
> but cannot verify this, which allows local users to cause a denial of 
> service (crash) or possibly gain privileges via a crafted system call.

> 在lINUX2.6.28以及之前版本的KERNEL中，架構為64位元的平台的ABI再要求System Call時，在
> User Space的Process若想將32位元的參數存放在64位元的暫存器時，要做到正確的sign 
> extension，但是在User Space的Process是無法保證能做到這一點，因此就有可能透過傳送參數
> 來導致系統崩潰或者以非常理的方式拿到更高的權限。

> 比如說一個函數具有32位元的int parameter，惡意的使用者可能會將一個值放入暫存器，但該值可能會大於int可能存在的值，且Compiler不易檢查到該值是否超出int的範圍。
> 為了解決上述問題，SYSCALL_DEFINE會先假定所有參數都是64bits的long value，然後再將它們
> 轉換回實際的類型!

> 在x86_64和Itanium，處理器當拿到一個32位元的值時，會忽略掉暫存器中的upper bits。

### copy_from_user & copy_to_user
```c=
ret = copy_from_user(va, initial, sizeof(*va)*n);
ret = copy_to_user(result, pa, sizeof(*pa)*n);
```
#### Overview
> user-space 無法「直接」存取 kernel-space 的記憶體，因此不能使用memcpy!

> 在syscall中，由於user space與kernel space算是不同的process且權限也不一樣，兩者之間的
> 記憶體是無法共用的，因此在傳遞變數時只傳值的話是無法改變變數的數值，所以才需要透過kernel的API: copy_to_user()和copy_from_user() 來傳遞參數。
> 前者是在kernel把位址傳回user space，後者則是將位址傳遞到kernel space。

#### copy_to_user
> to：資料的目的位址，此參數為一個指向 user-space 記憶體的指標。
> from：資料的來源位址，此參數為一個指向 kernel-space 記憶體的指標。
> **copy data to user-space from kernel-space**

#### copy_from_user
>  to：資料的目的位址，此參數為一個指向 kernel-space 記憶體的指標。
>  from：資料的來源位址，此參數為一個指向 user-space 記憶體的指標。
>  **copy data from user-space to kernel-space**
### Current 指標

```c=
#ifndef __ASM_GENERIC_CURRENT_H
#define __ASM_GENERIC_CURRENT_H

#include <linux/thread_info.h>

#define get_current() (current_thread_info()->task)
#define current get_current()

#endif /* __ASM_GENERIC_CURRENT_H */
```
>可以看出current實際上就是get_current()函數。
>get_current()又調用了current_thread_info()。
>以下是current_thread_info()的prototype
```c=
static inline struct thread_info *current_thread_info(void)
{
	register unsigned long sp asm ("sp");
	return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}
```
>當kernel thread執行到此處時，其SP的pointer指向調用process所对应的kernel thread的stack頂部。通過sp & ~(THREAD_SIZE-1)向上對齊，達到stack底部。
>將結果轉會為thread_info類型，此type中有一個成員为task_struct，它就是目前正在運行process的task_struct pointer。
* **程式碼解釋**
```c=
SYSCALL_DEFINE3(get_physical_addresses, unsigned long*, initial, unsigned long*, result, int , n)  
```
> initial = virtual address
> result = physical address
> int = # of VA and PA elements
> 

```c=
va = (unsigned long*)kmalloc(sizeof(unsigned long)*n,GFP_KERNEL);
pa = (unsigned long*)kmalloc(sizeof(unsigned long)*n,GFP_KERNEL); 
```
>-- kmalloc is the normal method of allocating memory for objects smaller than page size in the kernel.
>-- GFP_KERNEL: Allocate normal kernel ram.


```c=
copy_from_user(va, initial, sizeof(*va)*n);
```
>從user space code拷貝user space中的virtual address到kernel space中宣告的va


```c=
for (i = 0; i < n; i++)
{
        pgd = pgd_offset(current->mm, va[i]);
        pud = pud_offset((p4d_t*)pgd, va[i]);
        pmd = pmd_offset(pud, va[i]);
        pte = pte_offset_kernel(pmd, va[i]);

        page_addr = pte_val(*pte) & PAGE_MASK;
        page_offset = va[i] & ~PAGE_MASK;
        pa[i] = page_addr | page_offset;
}
```
>流程架構模擬如下圖

![](https://i.imgur.com/i3RsTDf.png)

>-- pgd_offset: 根據目前的virtual address和目前process的mm_struct，可得到pgd entry 
    (entry內容為pud table的 base address)
>-- pud_offset: 根據透過pgd_offset得到的pgd entry和virtual address，可得到pud entry
    (entry內容為pmd table的base address)
>-- pmd_offset: 根據pud entry的內容和virtual address，可得到pte table的base address
>-- pte_offset: 根據pmd entry的內容與virtual address，可得到pte的base address
>-- 將從pte得到的base address與Mask(0xf...fff000)做and運算，及可得到page的base physical address
>-- virtual address與~Mask(0x0...000fff)做and運算得到offset，再與page的base physical address做or運算即可得到轉換過後的完整的physical address。
>-- 將所得的physical address放入pa，繼續進行前面的動作，直到迴圈結束。

>以下為pgd_offset()相關macro
```c=
#define pgd_offset(mm, address) pgd_offset_pgd((mm)->pgd, (address))
#define pgd_offset_pgd(pgd, address) (pgd + pgd_index((address)))
//base addr + offset
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
//(1000000000 - 1 = 0111111111) 取後9個bits
#define PGDIR_SHIFT	39
#define PTRS_PER_PGD	512  //pgd的entry個數
```
>與上方架構圖對比，可以看出pgd_offset找出下一層pud的base address是用macro所寫的去找出entry。
>virtual address除了前面16-bit是unused之外，後面是照者9、9、9、9、12 bits去分割的(共48個bits)，從PGDIR_SHIFT可以看出來。
```c=
copy_to_user(result, pa, sizeof(*va)*n);
```
>將轉換完成的physical address從kernel space拷貝到user space中result所在的位置，這樣在user code中就可以取得我們所要的physical address。

```c=
kfree(va);
kfree(pa);
```
>最後記得要free掉當初kmalloc的記憶體，以免造成程式crash掉。


## User Space
### Code
```c=
#include <syscall.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include <stdlib.h>
#include <dlfcn.h>

int init_data = 30;
int non_init_data;
long ret = 0;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

void* our_func()
{
	pthread_mutex_lock( &mutex1 );
	int local_data = 30;
 	int* heapAddress = malloc(sizeof(int));
	void *fHandle;
	void *func;
	fHandle = dlopen("/lib/x86_64-linux-gnu/libc.so.6",RTLD_LAZY);
	printf("libc base: %lx\n",*(unsigned long*)fHandle);
	if (!fHandle) {
		printf ("%s\n", dlerror());
		exit(1);
	}
	func = dlsym(fHandle, "printf");
	static __thread int tls = 0;
	if(dlerror() != NULL){
		printf("%s\n", dlerror());
		exit(1);
	}
	size_t* va[7] = {
		(size_t*)&init_data,
		(size_t*)&non_init_data,
		(size_t*)&our_func,
		(size_t*)&local_data,
		(size_t*)func,
		(size_t*)heapAddress,
		(size_t*)&tls
	};
	size_t* pa[7];
	ret = syscall(333, va, pa, 7);
	printf("\nPid of the Thread is = %p, \nPid of the process is = %d\nAddresses info:", (size_t*)pthread_self(), getpid());
	char printArray [7][20] = {"Data", "BSS", "Code", "Stack", 
				   "Library", "heap", "thread"};
	for(int i=0;i<7;i++){
		printf("\n %d) %8s (va/pa) = %16p / %20p", i+1, printArray[i], va[i], pa[i]);
	}
	tls++;
	printf("\n\n");
	dlclose(fHandle);
	pthread_mutex_unlock( &mutex1 );
	pthread_exit(NULL);	

}

int main(){

	int local_data = 30;
	int* heapAddress = malloc(sizeof(int));
	char test = '\n';
	printf("start! %c\n", test);
	void* fHandle;
	void* func;
	fHandle = dlopen("/lib/x86_64-linux-gnu/libc.so.6",RTLD_LAZY);
	printf("libc base: %lx\n",*(size_t*)fHandle);
	if (!fHandle) {
		printf ("%s\n", dlerror());
		exit(1);
	}
	func = dlsym(fHandle, "printf");
	static __thread int tls = 0;
	if(dlerror() != NULL){
		printf("%s\n", dlerror());
		exit(1);
	}
	size_t* va[7] = {
		(size_t*)&init_data,
		(size_t*)&non_init_data,
		(size_t*)&our_func,
		(size_t*)&local_data,
		(size_t*)func,
		(size_t*)heapAddress,
		(size_t*)&tls
	};
	size_t* pa[7];
	ret = syscall(333, va, pa, 7);
	printf("Main thread\n");
	printf("\nPid of the Thread is = %p, \nPid of the process is = %d\nAddresses info:", (size_t*)pthread_self(), getpid());
	char printArray [7][20] = {"Data", "BSS", "Code", "Stack", 
				   "Library", "heap", "thread"};
	for(int i=0;i<7;i++){
		printf("\n %d) %8s (va/pa) = %16p / %20p", i+1, printArray[i], va[i], pa[i]);
	}
	tls++;
	printf("\n\n");
	pthread_t threads[3];
	dlclose(fHandle);
	for (int i = 0; i < 2; i++) {
		pthread_create(&threads[i], NULL, our_func, NULL);
	}
        sleep(2);
        printf("\n");
        for(int i=0;i<3;i++){
                long size = (long)&threads[i+1]-(long)&threads[i];
                printf("threads[%d]:\nsize:%d\nstart address:%p\nend address:%p\n\n", i, (int)size, &threads[i], (size_t*)((long)&threads[i]+size-1));
        }

	for (int i = 0; i < 2; i++) {
		pthread_join(threads[i], NULL);
	}

	if(ret > 0) printf("error!!!QQQQQQQ");

	return 0;
}
```

### Process
* **設計流程**
    >1. 使用 pthread_create 建立 3 個 thread
    >2. 使用 pthread_join 等待各 thread 結束
    >3. 使用 pthread_mutex_t 避免 thread 之間衝突
    >4. 使用 size_t* 型態存放 virtual address 和 physical address
    >5. 使用自定義 syscall 取得 va 中之實體位址並放入 pa
    >6. 印出各項 virtual address 和 physical address 作為結論分析依據
    >7. 利用 threads 陣列取得各 thread 的 size, start address, end address
* **在 global scope 觀察 Data 和 BSS 的記憶體位址**
    >1. 定義 init_data 以觀察 Data 的記憶體位址
    >2. 宣告 non_init_data 以觀察 BSS 的記憶體位址**
* **在 main 中觀察 stack, code, Thread-Local Storage, library 的記憶體位址**
    >1. 定義 local_data 以觀察 stack 的記憶體位址
    >2. 利用 dlopen 以 RTLD_LAZY 的方式取得動態連接庫位址，並藉由 dlsym 取得 printf 的記憶體位址
    >3. 用 &our_func 觀察 code 的記憶體位址
    >4. 觀察 tls 的記憶體位址是否改變
    >5. 定義 heapAddress，藉由 malloc 分配記憶體以觀察 heap 的記憶體位址
    >6. 宣告 threads 陣列，觀察在 process 的 stack 中 threads[i] 的大小、起始記憶體位址及結束的記憶體位址
* **在 our_func 觀察 stack, code, Thread-Local Storage, library 的記憶體位址**
    >1. 觀察 main 1~5 步驟中所提及之記憶體位址
* **pthread 和 fork 的差別**
    >1. pthread 為建立 thread 的函式，fork 為建立 process 的函式，而其在 linux 底層下都是執行 copy_process 去創建 process，差別只是在傳入的參數不同，以決定是否要複製(共用)當前 process 下的一些資訊
* **main thread 和 create_thread 中， mm_struct 和 task_struct 的差別**
    >1. 每個 process 都只會有一個 task_struct，用以存儲該 process 的各種資訊，如mm_struct, fs_struct, files_struct 等
    >2. mm_struct 記錄了該 process 相關的位址空間如 stack, MMAP, Heap, BBS等
    >3. 在 mm_struct 中，會有一個存放一個 stack 起始位置提供給 stack 存放空間。而如果是一個 Thread，會在 heap 或 mmap 內找一個空間作為 Thread 的 stack。
    >4. 無論是 fork 或是 pthread_create，task_struct 都會實例化，而在 thread 中 mm_struct 會與 main thread 共用

## 執行畫面
![](https://i.imgur.com/EbOVT3l.jpg)
![](https://i.imgur.com/JUiHsas.jpg)
- 可看出main thread與2個子thread的data, bss, library, code segment是為在相同的physical address起始位置上，而stack, thread local storage, heap segment的位址是不一樣的!
- thread local storage為每個thread獨立使用的空間，因此每個tls的位址會不一樣!
- 不管是main thread, thread1 or thread2，他們的PGID都是 18451。
- 每個thread的PID為不一樣的!
  >main thread = 0x7f8ffaaca700
  >thread1     = 0x7f8ffa0d2700
  >thread2     = 0x7f8ff98d1700
  >

## Result
![](https://i.imgur.com/Z5Scxij.jpg)

## Reference
-課堂提供的文件
https://view.officeapps.live.com/op/view.aspx?src=https%3A%2F%2Fstaff.csie.ncu.edu.tw%2Fhsufh%2FCOURSES%2FFALL2022%2Flinux_3.10_manual_2022.pptx&wdOrigin=BROWSELINK

https://view.officeapps.live.com/op/view.aspx?src=https%3A%2F%2Fstaff.csie.ncu.edu.tw%2Fhsufh%2FCOURSES%2FFALL2016%2Freminder.docx&wdOrigin=BROWSELINK

-Kernel code & System call
https://blog.kaibro.tw/2016/11/07/Linux-Kernel%E7%B7%A8%E8%AD%AF-Ubuntu/

https://dev.to/jasper/adding-a-system-call-to-the-linux-kernel-5-8-1-in-ubuntu-20-04-lts-2ga8

-CONFIG
https://blog.csdn.net/qq_36393978/article/details/118157426

https://stackoverflow.com/questions/61657707/btf-tmp-vmlinux-btf-pahole-pahole-is-not-available

-PGD, PMD, PTE
https://www.cnblogs.com/sky-heaven/p/5660210.html

-Multi-thread
https://haogroot.com/2020/12/20/pthread-note/#:~:text=%E9%80%9A%E5%B8%B8%E7%94%B1%20main%20thread%20%E4%BE%86%E5%88%86%E9%85%8D%E8%A8%98%E6%86%B6%E9%AB%94%E8%88%87%E9%87%8B%E6%94%BE%E8%A8%98%E6%86%B6%E9%AB%94%E6%9C%83%E6%98%AF%E6%AF%94%E8%BC%83%E5%A5%BD%E7%9A%84%E4%BD%9C%E6%B3%95%EF%BC%8C%E5%A6%82%E4%B8%8B%E9%9D%A2%E7%AF%84%E4%BE%8B%EF%BC%8C%E5%B0%87%E8%B3%87%E6%96%99%E5%A1%9E%E9%80%B2%E4%B8%80%E5%80%8B,struct%20%E8%A3%A1%EF%BC%8C%E7%B5%B1%E4%B8%80%E7%94%B1%20main%20thread%20%E7%AE%A1%E7%90%86%E8%A8%98%E6%86%B6%E9%AB%94%E3%80%82

https://ithelp.ithome.com.tw/articles/10280830

https://medium.com/@chinhung_liu/work-note-pthread-e908d53b557e

-ThreadID
https://stackoverflow.com/questions/21091000/how-to-get-thread-id-of-a-pthread-in-linux-c-program#:~:text=Linux%20provides%20such%20system%20call%20to%20allow%20you,gives%20you%20pid%20%2C%20each%20threadid%20and%20spid.

-位址轉換圖片來源
https://www.chegg.com/homework-help/questions-and-answers/given-virtual-memory-areas-used-process-write-system-call-sysgetpageinfo-report-current-st-q46483922

https://0xax.gitbooks.io/linux-insides/content/Theory/linux-theory-1.html

-SYSCALL_DEFINE
https://nvd.nist.gov/vuln/detail/CVE-2009-0029

https://blog.csdn.net/hxmhyp/article/details/22699669

https://blog.csdn.net/hxmhyp/article/details/22619729

https://www.quora.com/What-is-Kernel-ABI

https://stackoverflow.com/questions/18885045/confusion-about-cve-2009-0029-linux-kernel-insecure-64-bit-system-call-argument

-current 指標
https://blog.csdn.net/u010154760/article/details/45676723








