본문 바로가기

Core BSP 분석/커널 트러블슈팅

[Kernel][Stability] tcp_v4_rcv -> __stack_chk_fail

#커널 크래시 디버깅 및 TroubleShooting
 
커널 패닉 시 아래 콜 스택이 남아 있습니다.
[39136682.663247]  <IRQ>  [<ffffffff816351f1>] dump_stack+0x19/0x1b
[39136682.663626]  [<ffffffff8162ea6c>] panic+0xd8/0x1e7
[39136682.663988]  [<ffffffff8158bd35>] ? tcp_v4_rcv+0x635/0x9f0
[39136682.664361]  [<ffffffff8107b00b>] __stack_chk_fail+0x1b/0x20
[39136682.664743]  [<ffffffff8158bd35>] tcp_v4_rcv+0x635/0x9f0
[39136682.665131]  [<ffffffff8163d0ed>] common_interrupt+0x6d/0x6d
[39136682.665529]  <EOI>  [<ffffffff81645a2b>] ? sysret_audit+0x17/0x21
 
위 콜 스택을 보면 여러가지 의문이 생기는데요.
1> tcp_v4_rcv이 인터럽트 핸들러로 호출되는 것 처럼 보이는데, 다른 인터럽트 핸들러도 위와 같은 콜 스택이 확인되나요?
    다른 인터럽트 핸들러가 호출될 때와 다른 콜스택이면 스택 Corruption을 의심할 수 있습니다.
    
2> 커널 패닉이 발생했을 때 RSP 즉 스택 주소(00002aaec5008b98)가 이상합니다. 
커널 프로세스의 스택 주소는 struct task_struct->stack -- (struct task_struct->stack)+0x4000 사이이어야 합니다.
crash> bt
PID: 168037 TASK: ffff880494923980 CPU: 26 COMMAND: "app01"
#0 [ffff88046f3a3db8] machine_kexec at ffffffff81051beb
#1 [ffff88046f3a3e18] crash_kexec at ffffffff810f2542
#2 [ffff88046f3a3ee8] panic at ffffffff8162ea73
#3 [ffff88046f3a3f68] __stack_chk_fail at ffffffff8107b00b
#4 [ffff88046f3a3f78] tcp_v4_rcv at ffffffff8158bd35
--- <IRQ stack> ---
#5 [ffff880217dbbf58] ret_from_intr at ffffffff8163d0ed
RIP: 00002aae21629848 RSP: 00002aaec5008b98 RFLAGS: 00000202
RAX: 00002aaf8c0394c0 RBX: ffffffff81645a2b RCX: 0000000000038400
RDX: 0000000000038180 RSI: 00002aaf68157390 RDI: 00002aaf8c0716c0
RBP: 00002aaec5009180 R8: 00002aaf8c0718b0 R9: 0000000000000000
R10: 0000000000000002 R11: 00002aae21657160 R12: 0000000000000000
R13: 00002aaec500a700 R14: 00002aaec500a9c0 R15: 0000000000000000
ORIG_RAX: ffffffffffffffca CS: 0033 SS: 002b
 
아래 명령어로 스택 주소를 확인해보세요.
struct task_struct.stack ffff880494923980
 
예)
crash64> struct task_struct.stack ffffffc001580e40
  stack = 0xffffffc001574000 <init_thread_union>
 
3> 만약 2>에서 언급된 것과 같이 스택이 꺠져 있다면, 아래 방법으로 tcp_v4_rcv 함수 내에서 호출되는 함수 전 후로 로그를 확인해도 좋을 것 같습니다. 
 
4> 커널 Makefile을 보면 -fstack-protector 옵션이 켜져 있을 때 스택 카나리 코드가 추가됩니다. 아래 옵션을 한 번 끄고 어떤 동작을 하는 지 점검해도 좋을 것 같습니다.
 ifdef CONFIG_CC_STACKPROTECTOR_REGULAR
   stackp-flag := -fstack-protector
   ifeq ($(call cc-option, $(stackp-flag)),)
     $(warning Cannot use CONFIG_CC_STACKPROTECTOR_REGULAR: \
              -fstack-protector not supported by compiler)
   endif
 else
 ifdef CONFIG_CC_STACKPROTECTOR_STRONG
   stackp-flag := -fstack-protector-strong
   ifeq ($(call cc-option, $(stackp-flag)),)
     $(warning Cannot use CONFIG_CC_STACKPROTECTOR_STRONG: \
               -fstack-protector-strong not supported by compiler)
   endif
 else
   # Force off for distro compilers that enable stack protector by default.
   stackp-flag := $(call cc-option, -fno-stack-protector)
 
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2