Byeo

Linux System Call 추가하기 본문

프로그래밍 (Programming)/네트워크 스택

Linux System Call 추가하기

BKlee 2024. 3. 16. 14:31
반응형

개요

리눅스 코드를 살펴보고 구현해보는 과정을 간단히 체험해보기 위해서 가장 쉬운 시스템 콜 추가하기를 해봅니다.

해당 과정을 위해 [블로그]와 [커널 문서]를 참고하였습니다. 커널 문서에서는 시스템 콜 구현 디자인 가이드라인을 제공하고 있습니다.

 

 

내용

1. syscall_64.tbl

linux-5.15/arch/x86/entry/syscalls/syscall_64.tbl 파일에는 x86 아키텍쳐에서 사용할 수 있는 시스템콜이 나열되어 있습니다. 형태는 다음과 같습니다.

# The format is:
# <number> <abi> <name> <entry point>

 

가장 마지막에 있는 448 다음에 우리가 추가할 시스템 콜 이름과 실제 커널 내에서 호출할 함수 명을 나열해줍시다.

449	common	byeo_hello		sys_byeo_hello

 

 

2. sys_byeo_hello 구현하기

linux-5.15/kernel 디렉토리 내에 byeo.c를 생성하여 다음을 구현합니다. 

 

 ▶ 문자열과 길이를 전달받아 문자열을 출력하는 함수를 구현하였습니다. 단, 길이가 100 이상인지, 그리고 문자열의 마지막에 NULL이 오는지 체크합니다.

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/syscalls.h>

long __sys_byeo_hello(char __user *user_string, int strlen)
{
    char kernel_copied_string[100];
    int ret = 0; 
    printk("sys_byeo_hello parameter given, %p, %d", (void*) user_string, strlen);

    if (strlen > 99) {
	return -EINVAL;
    }

    ret = __copy_from_user(kernel_copied_string, user_string, strlen + 1);
    if (ret != 0) {
	return -EFAULT;
    }

    if (kernel_copied_string[strlen] != 0) {
	return -EFAULT;
    }
    
    printk("Hello, %s", kernel_copied_string);
    return 0;
}


SYSCALL_DEFINE2(byeo_hello, char __user*, user_string, int, strlen)
{
	return __sys_byeo_hello(user_string, strlen);
}

 

linux-5.15/syscalls.h에 함수 헤더를 추가합니다.

asmlinkage long sys_byeo_hello(char __user *user_string, int strlen);

 

 

3. Makefile 추가하기

linux-5.15/kernel/Makefile에 다음과 같이 file명.o 를 추가해줍니다.

 

 

4. Make 실행

make -j`nproc`

을 실행합니다.

 

5. qemu 실행

./qemu.sh를 이용해 실행해줍니다. 해당 스크립트 내의 내용은 [포스트]에 있습니다.

 

package를 몇 개 설치합니다. (net-tools, vim, gcc)

VM의 네트워크 연결은 [QEMU-VM의-네트워크를-인터넷과-연결하기] 포스트를 참고해주세요!

 

6. User application 작성

6-1. 시스템콜의 올바른 사용

QEMU가 실행되고 나면, 원하는 폴더에 다음과 같이 코드를 작성하고 gcc로 컴파일합니다!

#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <errno.h>

int main() {
        char str[100] = "BYEO's user string";
        int length = strlen(str);
        printf("user_string: %s ,Len: %d\n", str, length);

        int ret = syscall(449, str, length);
        printf("syscall return value: %d, errno: %d\n", ret, errno);
        return 0;
}

 

실행해봅시다. 

root@bklee-qemu:~/practice# ./a.out
user_string: BYEO's user string ,Len: 18
syscall return value: 0, errno: 0

 

dmesg를 확인해봅니다. printk는 dmesg에 기록이 남습니다.

[  211.280114] sys_byeo_hello parameter given, 00000000c5fc0152, 18
[  211.280127] Hello, BYEO's user string

 

6-2. 길이 100 초과

root@bklee-qemu:~/practice# !./
./a.out
user_string: BYEO's user string BYEO's user string BYEO's user string BYEO's user string BYEO's user string BYEO's user stringg
,Len: 189
syscall return value: -1, errno: 22

 

systemcall이 -1을 return받았습니다. 그리고 errno도 -EINVAL (22)를 잘 뱉는군요.

 

6-3. Without NULL character termination

root@bklee-qemu:~/practice# ./a.out
user_string: BYEO's user stringAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ,Len: 18
syscall return value: -1, errno: 14

strlen을 계산한 뒤, 유저의 문자열에 전부 'A'를 채워넣었습니다. errno가 -EFAULT (14)로 잘 나오는 군요.

 

7. Troubleshooting

7-1. undefined reference

ld: arch/x86/entry/syscall_64.o:(.rodata+0xe08): undefined reference to `__x64_sys_byeo_hello'
make: *** [Makefile:1183: vmlinux] Error 1

 

만약 컴파일 과정중에 위와 같은 결과가 나온다면 절차에 문제가 있을 확률이 높습니다! (저는 임의로 syscalls_64.h를 건드렸다가 발생했었습니다.)

 

7-2. NULL pointer dereference panic

BUG: unable to handle kernel NULL pointer dereference at (null)

 

직접 겪어보니 헤더 선언은 잘 되었는데, 실제 코드를 참조하지 못해서 발생할 수 있는 에러같습니다. 시스템콜 이름이 올바르게 설정되었는지 체크를 한 번 해본 결과, 처음에 SYSCALL_DEFINE에 함수명을 잘못넣었었습니다.

SYSCALL_DEFINE2(sys_byeo_hello, char __user*, user_string, int, strlen) # WRONG
SYSCALL_DEFINE2(byeo_hello, char __user*, user_string, int, strlen) # CORRECT

 

Linux Kernel version마다 차이가 있을 수도 있습니다. 가장 좋은 방법은 작업 중인 동일 커널 버전 내에서 다른 시스템 콜의 선언 방식을 따라하는 것일 듯 합니다.

 

7-3. user-memory-access

 BUG: KASAN: user-memory-access in __sys_byeo_hello+0x41/0x88

 

__copy_from_user 없이 user memory를 직접 접근할 때 발생하는 에러입니다. 

 

 

8. 마무리

Linux Kernel에 소소한 소스코드를 하나 넣어보았습니다. 이제 이것으로 무엇을 할 지는 천천히 생각해봐야겠습니다~

 

참고 자료:

시스템콜 호출 과정: https://ia800404.us.archive.org/27/items/linux-insides/linux-insides.pdf

시스템콜 추가 방법 포스트: https://junshim.github.io/linux%20kernel%20study/Add_a_New_System_Call/

시스템콜 추가에 대한 가이드라인: https://www.kernel.org/doc/html/latest/process/adding-syscalls.html

errno: https://en.wikipedia.org/wiki/Errno.h

반응형
Comments