Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

86235
1036
103656


[라즈베리파이] 시그널 - 커널 공간 시그널 함수 분석 - 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 시그널
시그널이란
시그널 설정은 어떻게 할까
시그널 생성 과정 함수 분석
프로세스는 언제 시그널을 받을까
시그널 전달과 처리는 어떻게 할까?
시그널 제어 suspend() 함수 분석 
시그널 ftrace 디버깅

"혹시 궁금점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답글 올려드리겠습니다!" 

 
 

핑백

덧글

댓글 입력 영역