Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

125187
803
94454


[라즈베리파이] 시그널 - 커널 공간 시그널 함수 분석 - do_sigaction() 12장. 시그널

다음으로 살펴볼 코드는 do_sigaction() 함수입니다. 먼저 do_sigaction() 함수는 선언부를 봅시다.
int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact);

do_sigaction() 함수에 전달하는 인자 속성은 다음과 같은데 sys_rt_sigaction() 함수에 전달된 인자 유형과 같습니다.
int sig: 설정한 시그널 번호
struct k_sigaction *act: 새롭게 설정하는 시그널 속성
struct k_sigaction *oact: 이전에 설정했던 시그널 속성

do_sigaction() 함수 인자를 알아봤으니 코드를 분석할 차례입니다.
1 int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
2 {
3 struct task_struct *p = current, *t;
4 struct k_sigaction *k;
5 sigset_t mask;
6
7 if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
8 return -EINVAL;
9
10 k = &p->sighand->action[sig-1];
11
12 spin_lock_irq(&p->sighand->siglock);
13 if (oact)
14 *oact = *k;
15
16 sigaction_compat_abi(act, oact);
17
18 if (act) {
19 sigdelsetmask(&act->sa.sa_mask,
20       sigmask(SIGKILL) | sigmask(SIGSTOP));
21 *k = *act;
22
23 if (sig_handler_ignored(sig_handler(p, sig), sig)) {
24 sigemptyset(&mask);
25 sigaddset(&mask, sig);
26 flush_sigqueue_mask(&mask, &p->signal->shared_pending);
27 for_each_thread(p, t)
28 flush_sigqueue_mask(&mask, &t->pending);
29 }
30 }
31
32 spin_unlock_irq(&p->sighand->siglock);
33 return 0;
34 }

7번째 줄 코드를 보겠습니다. 인자로 전달된 정수형 시그널 번호에 오류가 있는지 점검하는 예외처리 코드입니다. 유저 공간에서 정의되어 있지 않은 시그널 번호를 함수 인자로 전달할 수 있기 때문입니다.
7 if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
8 return -EINVAL;

이 조건을 만족하면 -EINVAL를 반환하고 함수 실행을 종료하면서 시그널 설정을 더 이상 진행하지 않습니다.

10번째 줄 코드를 보겠습니다.
3 struct task_struct *p = current, *t;
4 struct k_sigaction *k;
..
10 k = &p->sighand->action[sig-1];

현재 실행 중인 태스크 디스크립터 주소를 저장하고 있는 p이란 지역 변수로 시그널 번호인 sig에 해당하는 action 멤버 배열을 k이란 struct k_sigaction 구조체 지역 변수에 저장합니다.

struct task_struct 구조체 멤버 sighand->action[] 에는 시그널 종류 별로 어떤 동작을 처리할지에 대한 설정 정보가 저장돼 있습니다.

Trace32로 &p->sighand->action 멤버를 확인하면 다음과 같습니다.
1  (static struct task_struct) \Global\init_task = (
2    (long int) state = 0 = 0x0,
3    (void *) stack = 0x80C00000,
...
4    (struct sighand_struct *) sighand = 0x80C07B80 -> (
5      (atomic_t) count = ((int) counter = 3 = 0x3),
6      (struct k_sigaction [64]) action = (
7         [0] = ((struct sigaction) sa = ((__sighandler_t)sa_handler = 0x0, (long unsigned int) sa_flags = 0
8         [1] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x0, (long unsigned int) sa_flags = 0
9         [2] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x7AAE00B4, (long unsigned int) sa_flags = 0
10       [3] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x7AAE00B4, (long unsigned int) sa_fla
11       [4] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x0, (long unsigned int) sa_fla

...
12        [13] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x0, (long unsigned int) sa_flags = 0
12        [63] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x0, (long unsigned int) sa_flags = 0

action이란 배열의 크기는 64개이고4번째 줄 정보에 보이는 struct sighand_struct 구조체 멤버 중 하나입니다. 

다음과 같이 2~3번째 배열을 보면 sa_handler 멤버에서 주소를 볼 수 있습니다. 2번째 배열은 SIGINT, 3번째 배열은 SIGQUIT 시그널을 의미하며 유저 공간에서 설정된 시그널 핸들러 주소가 0x7AAE00B4임을 알 수 있습니다.
9 [2] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x7AAE00B4, (long unsigned int) sa_flags = 0
10 [3] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x7AAE00B4, (long unsigned int) sa_fla

다음 코드는 SIGINT와 SIGQUIT 시그널 번호 선언부입니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/arch/arm/include/uapi/asm/signal.h]
#define SIGINT  2
#define SIGQUIT  3

다음 13~14번째 줄 코드를 보겠습니다.
13 if (oact)
14 *oact = *k;

현재 실행 중인 프로세스에서 읽어온 k이란 포인터를 oact에 저장합니다.
k는 시그널 번호에 해당하는 struct k_sigaction 구조체인데 oact 포인터에 저장하는 동작은 기존에 설정된 시그널 속성을 oact 포인터로 저장한다는 의미입니다.

다음 18번째 줄 코드를 보겠습니다. 이 함수에서 가장 중요한 동작입니다.
18 if (act) {
19 sigdelsetmask(&act->sa.sa_mask,
20       sigmask(SIGKILL) | sigmask(SIGSTOP));
21 *k = *act;

do_sigaction() 함수로 전달된 act이란 인자는 새롭게 설정하는 시그널 속성을 의미합니다.
이를 k이란 포인터에 저장합니다. 이는 새롭게 설정한 struct k_action 구조체를 프로세스 태스크 디스크립터인 struct task_struct 구조체 내 sighand->action 배열에 저장한다는 의미입니다.

k이란 지역 변수 출처를 다시 확인하겠습니다.
3 struct task_struct *p = current, *t;
..
10 k = &p->sighand->action[sig-1];

가장 중요한 sys_rt_sigaction() 함수가 실행한 다음 변경되는 시그널 관련 자료 구조를 Trace32로 살펴봅시다.
1  (static struct task_struct) \Global\init_task = (
2    (long int) state = 0 = 0x0,
3    (void *) stack = 0x80C00000,
...
4    (struct sighand_struct *) sighand = 0x80C07B80 -> (
5      (atomic_t) count = ((int) counter = 3 = 0x3),
6      (struct k_sigaction [64]) action = (
7         [0] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x0, (long unsigned int) sa_flags = 0
8         [1] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x0, (long unsigned int) sa_flags = 0
9         [2] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x7AAE11AC, (long unsigned int) sa_flags = 0
10       [3] = ((struct sigaction) sa = ((__sighandler_t) sa_handler = 0x7AAE00B4, (long unsigned int) sa_fla

시그널을 설정하면 시그널을 처리할 프로세스의 태스크 디스크립터 struct task_struct sighand->action[] 배열이 업데이트됩니다. 프로세스 태스크 디스크립터 sighand->action[64] 배열 중 시그널 번호에 해당하는 멤버가 변경된다는 것입니다.

만약 2번 SIGINT 시그널을 설정했고 시그널 핸들러 주소가 0x7AAE11AC이면 위와 같이 9번째 줄 정보와 같이 업데이트됩니다.

이렇게 시그널을 설정하고 난 후 SIGINT 시그널이 발생하면 프로세스가 시그널을 받아서 struct task_struct sighand->action[] 배열에 저장된 속성에 따라 시그널을 처리합니다.

#Referene 시그널
시그널이란
시그널 설정은 어떻게 할까
   sys_pause() 함수 분석
시그널 생성 과정 함수 분석
   유저 프로세스 kill() 함수 실행
   유저 프로세스 tgkill()함수 실행
   커널은 언제 시그널 생성할까?
   __send_signal() 함수 분석
시그널 전달 진입점
   ret_fast_syscall 레이블 분석
   인터럽트 벡터
시그널 전달과 처리는 어떻게 할까?
   get_signal() 함수 분석
   handle_signal() 함수 분석
시그널 제어 함수 분석
   suspend() 함수
시그널 ftrace 디버깅
   ftrace 시그널 기본 동작 로그 분석
   ftrace 시그널 핸들러 동작 로그 분석

#Reference 시스템 콜


Reference(워크큐)
워크큐(Workqueue) Overview



핑백

덧글

댓글 입력 영역