Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

230224
1178
109352


tombstone 시(시스템 크래시) - 커널 패닉 유발 [Debugging] Tips

전 리눅스 커널 및 드라이버 코드를 주로 보는데요. 그런데 리눅스 시스템 프로그램 코드가 자주 봐야 해요.

아 그리고 userspace에서 tombstone(무덤)이 떨어지면서 크래시가 종종 발생하거든요. 이런 이슈도 잡아야 되요.
에러 시그니처는 아래와 같아요. 흠...
Revision: '0'
ABI: 'arm'
pid: 1558, tid: 1891, name: RenderThread >>> com.google.launcher2 <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
r0 00000000 r1 80808080 r2 00000000 r3 00000000
r4 9a979f0c r5 9e7e474c r6 00000000 r7 00000000
backtrace:
#00 pc 00016198 /system/lib/libc.so (strlen+33)
#01 pc 0025b531 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#02 pc 0025b5cb /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#03 pc 0025511f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#04 pc 00240fb7 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#05 pc 00214d67 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#06 pc 00212f1f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#07 pc 0021305f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so

이럴 때는 보통 HAL(Hardware Adaptation Layer) 혹은 데몬 코드 리뷰를 하면서 문제점을 점검하며 크래시를 잡는데요.

그런데 위와 같이 유저 공간에서 크래시가 발생할 때 커널 패닉을 유발시켜서 코어 덤프를 뜨고 싶을 때가 있어요.
어떤 경우 이런 짓을 하고 싶을까요?

뭐 mmap으로 특정 하드웨어를 제어하는 시스템 콜 이후 tombstone이 떨어지던가,
아니면 같은 시간대에 커널 로그를 보고 싶은 경우가 가끔 있어요.

아주 가끔은 T32 잘 쓰는 자랑질을 하기 위해서, 
T32 장비를 타겟 디바이스에 연결 후 T32로 break point를 걸고 유저 공간까지 콜스택 까지 볼 수도 있죠.

자, 일단 커널 Config를 아래와 같이 설정하고요.
diff --git a/arch/arm/configs/pompeii_defconfig b/arch/arm/configs/pompeii_defconfig
index ed1d990..4065c00 100644
--- a/arch/arm/configs/pompeii_defconfig
+++ b/arch/arm/configs/pompeii_defconfig
@@ -777,6 +777,7 @@ CONFIG_DEBUG_SET_MODULE_RONX=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_SECURITY_NETWORK=y
+CONFIG_DEBUG_USER=y
 CONFIG_DYNAMIC_FTRACE=y

부트로더에서 "user_debug=24"로 커널 커맨드 라인을 전달해요.
 static void target_common_update_static_bootcmd(void)
 {
+    bootcmd_add_pair("user_debug","24");
+

그러면 커널 부팅 시에 아래 코드에서 user_debug 변수를 24로 설정하죠.
[kernel/arch/arm/kernel/traps.c]
#ifdef CONFIG_DEBUG_USER
unsigned int user_debug;

static int __init user_debug_setup(char *str)
{
get_option(&str, &user_debug);
return 1;
}
__setup("user_debug=", user_debug_setup);
#endif

user_debug 변수를 24로 왜 설정하냐구요? 아래 매크로를 모두 OR 연산 해보면 알 수 있죠.
#define UDBG_UNDEFINED (1 << 0)
#define UDBG_SYSCALL (1 << 1)
#define UDBG_BADABORT (1 << 2)
#define UDBG_SEGV (1 << 3)
#define UDBG_BUS (1 << 4)


이제 마지막으로, 커널이 부팅하고 나면, 아래와 같이 print-fatal-signals 파일을 1로 설정합니다.
echo 1 > /proc/sys/kernel/print-fatal-signals

이제 끝...

100% 유저 스페이스에서 tombstone이 떨어지는 디바이스에서 위 설정을 적용하고 실행해보았더니, 아래와 같이 바로 커널 패닉으로 떨어지네요.
참 친절하게도 유저 공간의 프로그램 카운터(PC)와 스택 정보를 찍어주네요.
<7>[   72.248793 / 05-21 22:27:31.925][2] android.ui: unhandled page fault (11) at 0x00000000, code 0x005
<1>[   72.248810 / 05-21 22:27:31.925][2] pgd = da0fc000
<1>[   72.252580 / 05-21 22:27:31.925][2] [00000000] *pgd=00000000
<6>[   72.258764 / 05-21 22:27:31.935][2] CPU: 2 PID: 999 Comm: android.ui Tainted: G        W      3.18.66-gbbd0bc4-dirty #3
<6>[   72.258781 / 05-21 22:27:31.935][2] task: d7e0d840 ti: d7f02000 task.ti: d7f02000
<6>[   72.258793 / 05-21 22:27:31.935][2] PC is at 0x73a7c9c0
<6>[   72.258803 / 05-21 22:27:31.935][2] LR is at 0x0
<6>[   72.258814 / 05-21 22:27:31.935][2] pc : [<73a7c9c0>]    lr : [<00000000>]    psr: 00070030
<6>[   72.258814 / 05-21 22:27:31.935][2] sp : 885bc480  ip : 00000000  fp : 131e3218
<6>[   72.258830 / 05-21 22:27:31.935][2] r10: 71488688  r9 : 92778200  r8 : 00000000
<6>[   72.258840 / 05-21 22:27:31.935][2] r7 : 131e2bf0  r6 : 00000000  r5 : 00000001  r4 : 00263c64
<6>[   72.258851 / 05-21 22:27:31.935][2] r3 : 131e3218  r2 : 71488688  r1 : 00000000  r0 : 71488688
<6>[   72.258863 / 05-21 22:27:31.935][2] Flags: nzcv  IRQs on  FIQs on  Mode USER_32  ISA Thumb  Segment user
<6>[   72.258873 / 05-21 22:27:31.935][2] Control: 10c0383d  Table: 9a0fc06a  DAC: 00000051
<6>[   72.258885 / 05-21 22:27:31.935][2] CPU: 2 PID: 999 Comm: android.ui Tainted: G        W      3.18.66-gbbd0bc4-dirty #3
<6>[   72.258907 / 05-21 22:27:31.935][2] [<c010e400>] (unwind_backtrace) from [<c010b4a8>] (show_stack+0x10/0x14)
<6>[   72.258924 / 05-21 22:27:31.935][2] [<c010b4a8>] (show_stack) from [<c0fd0740>] (dump_stack+0x78/0x98)
<6>[   72.258940 / 05-21 22:27:31.935][2] [<c0fd0740>] (dump_stack) from [<c0115f8c>] (__do_user_fault+0x108/0x19c)
<6>[   72.258957 / 05-21 22:27:31.935][2] [<c0115f8c>] (__do_user_fault) from [<c0fe05b8>] (do_page_fault+0x33c/0x3e8)
<6>[   72.258972 / 05-21 22:27:31.935][2] [<c0fe05b8>] (do_page_fault) from [<c0100404>] (do_DataAbort+0x34/0x184)
<6>[   72.258986 / 05-21 22:27:31.935][2] [<c0100404>] (do_DataAbort) from [<c0fdec3c>] (__dabt_usr+0x3c/0x40)
<6>[   72.258997 / 05-21 22:27:31.935][2] Exception stack(0xd7f03fb0 to 0xd7f03ff8)
<6>[   72.259009 / 05-21 22:27:31.935][2] 3fa0:                                     71488688 00000000 71488688 131e3218
<6>[   72.259023 / 05-21 22:27:31.935][2] 3fc0: 00263c64 00000001 00000000 131e2bf0 00000000 92778200 71488688 131e3218
<6>[   72.259036 / 05-21 22:27:31.935][2] 3fe0: 00000000 885bc480 00000000 73a7c9c0 00070030 ffffffff

이럴 때 어느 함수에 T32 브레이크 포인트를 걸어야 할까요?
__do_user_fault() 함수의 CONFIG_DEBUG_USER 안에 걸며 딱이겠죠.
static void
__do_user_fault(struct task_struct *tsk, unsigned long addr,
unsigned int fsr, unsigned int sig, int code,
struct pt_regs *regs)
{
struct siginfo si;

trace_user_fault(tsk, addr, fsr);

#ifdef CONFIG_DEBUG_USER
if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) ||  //<<-- 여기 브레이크 포인트
    ((user_debug & UDBG_BUS)  && (sig == SIGBUS))) {
printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
       tsk->comm, sig, addr, fsr);
show_pte(tsk->mm, addr);
show_regs(regs);
}
#endif

__do_user_fault() 함수를 호출하는 루틴
static int __kprobes
do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
// ... 생략 ...

if (user_mode(regs))
flags |= FAULT_FLAG_USER;
if (fsr & FSR_WRITE)
flags |= FAULT_FLAG_WRITE;

// .. 생략 ...

__do_user_fault(tsk, addr, fsr, sig, code, regs);
return 0;

 no_context:
__do_kernel_fault(mm, addr, fsr, regs);
return 0;
}

가끔 T32 잘 쓴다고 자랑해보세요.

Reference(프로세스 관리)
4.9 프로세스 컨택스트 정보는 어떻게 저장할까?
 4.9.1 컨택스트 소개
 4.9.2 인터럽트 컨택스트 정보 확인하기
 4.9.3 Soft IRQ 컨택스트 정보 확인하기
 4.9.4 선점 스케줄링 여부 정보 저장
4.10 프로세스 디스크립터 접근 매크로 함수
 4.10.1 current_thread_info()
 4.10.2 current 매크로란
4.11 프로세스 디버깅
 4.11.1 glibc fork 함수 gdb 디버깅
 4.11.2 유저 프로그램 실행 추적 

덧글

댓글 입력 영역