call_usermodehelper() 사용법

Linux 커널은 일반적으로 사용자 공간(user space)에서 동작하는 프로그램을 직접 실행하지 않는다. 그러나 특정 상황에서는 커널 모듈 또는 커널 코드에서 사용자 공간의 프로그램을 실행해야 할 필요가 때가 있다. 예를 들어, 커널에서 특정 시점에 로그를 외부로 전송하거나, 오류 발생 시 자동 복구 스크립트를 호출하는 등이 그 예이다. 이러한 기능을 위해 커널은 **call_usermodehelper()**라는 API를 제공한다. 이 글에서는 call_usermodehelper()의 기본 개념과 사용법에 대해 알아본다.


call_usermodehelper() ?

call_usermodehelper()는 커널 코드에서 사용자 공간의 프로그램을 실행할 수 있게 해주는 함수이다. 이는 커널이 명시적으로 지정한 바이너리와 인자, 환경 변수를 가지고 사용자 공간의 프로그램을 실행한다.

int call_usermodehelper(const char *path, char **argv, char **envp, int wait);

각 매개 변수는 다음과 같다.

매개변수설명
path실행할 사용자 공간 바이너리의 경로 (예: /bin/echo)
argv실행 인자 배열 (argv[0]은 프로그램 이름)
envp환경 변수 배열
wait실행 방식: 비동기/동기 선택 (UMH_NO_WAIT, UMH_WAIT_PROC, 등)

주요 사용 방식

wait 플래그

플래그 이름설명
UMH_NO_WAIT비동기로 실행. 즉시 반환
UMH_WAIT_PROC자식 프로세스 종료 시까지 대기
UMH_WAIT_EXEC자식 프로세스가 execve() 호출 후 반환

일반적으로 가장 많이 사용하는 옵션은 UMH_WAIT_PROC이다.


Example: echo 실행

커널 모듈에서 /bin/echo hello kernel 명령어를 실행

커널 모듈 예제 코드

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kmod.h>

static int __init my_module_init(void)
{
    char *argv[] = { "/bin/echo", "hello kernel", NULL };
    char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };

    int ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);

    printk(KERN_INFO "call_usermodehelper returned: %d\n", ret);
    return 0;
}

static void __exit my_module_exit(void)
{
    printk(KERN_INFO "Module exit.\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("call_usermodehelper Example");
MODULE_AUTHOR("YourName");

Makefile 예제

obj-m += call_helper.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

빌드 및 실행

make
sudo insmod call_helper.ko
dmesg | tail

결과

hello kernel
call_usermodehelper returned: 0

call_usermodehelper_setup / exec 방식

보다 세부적으로 사용자 공간 프로세스를 컨트롤할 수 있도록 call_usermodehelper_setup()call_usermodehelper_exec() 함수를 사용할 수도 있다.

Example: call_usermodehelper_setup 사용

#include <linux/module.h>
#include <linux/kmod.h>

static int __init mod_init(void)
{
    struct subprocess_info *info;
    char *argv[] = { "/usr/bin/logger", "Hello from kernel", NULL };
    char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };

    info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC, NULL, NULL, NULL);
    if (!info)
        return -ENOMEM;

    return call_usermodehelper_exec(info, UMH_WAIT_PROC);
}

static void __exit mod_exit(void)
{
    printk(KERN_INFO "Exiting module.\n");
}

module_init(mod_init);
module_exit(mod_exit);

MODULE_LICENSE("GPL");

call_usermodehelper 작동 흐름

+----------------------+
| 커널 공간 (Kernel) |
+----------+-----------+
|
| call_usermodehelper()
v
+--------------------------+
| subprocess_info 구조체 |
| exec path, argv, envp 등 |
+--------------------------+
|
| fork() + execve()
v
+----------------------+
| 사용자 공간 프로그램 |
+----------------------+

주의사항

1. 실행 파일의 경로 문제

  • /bin/echo 같은 명령어는 경로를 정확히 지정해야 하며, PATH 환경 변수는 명시적으로 제공해야 한다.

2. 커널 로그 외부로 출력

  • printk() 메시지는 dmesg에 기록된다.
  • 사용자 공간에서 로그를 기록하려면 logger 명령이나 /dev/kmsg에 쓰는 방식이 필요하다.

3. 사용자 공간 의존성

  • 사용자 공간 프로그램이 없어도 커널 모듈은 로드되지만, 실행 시 실패할 수 있다.
  • BusyBox 환경 등에서는 경로가 다를 수 있으므로 반드시 존재 여부 확인 필요.

4. 권한 제한

  • 루트 권한으로 동작하므로 신중히 사용해야 한다.
  • 보안 이슈가 될 수 있으므로 외부 입력값을 통한 command 실행은 항상 주의해야 한다.

Example: 스크립트 실행

/usr/local/bin/myhook.sh 실행

char *argv[] = { "/usr/local/bin/myhook.sh", "arg1", "arg2", NULL };
char *envp[] = { "HOME=/", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
int ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);

스크립트 예

#!/bin/bash
echo "call_usermodehelper called with $1 and $2" >> /tmp/kernel.log

반드시 chmod +x로 실행 권한 부여


디버깅 방법

1. dmesg 출력 확인

dmesg | tail -n 50

2. 프로그램 실행 실패 확인

strace -e execve /bin/echo hello

3. 로그 파일로 출력 유도

char *argv[] = { "/usr/bin/logger", "Kernel event happened", NULL };

call_usermodehelper 사용 시의 보안 고려사항

  • 입력 값 검증: 사용자 입력을 통한 경로 지정은 절대 금지.
  • 실행 제한: root 권한을 사용하므로 반드시 신뢰된 프로그램만 실행.
  • SElinux / AppArmor 정책: 사용자 공간 실행 제한을 적용할 수 있음.

참고 사이트

답글 남기기