Oops analysis in ARM Mar 3, 2009

이 글은 barrios의 좋지 않은 기억력을 보강하기 위해 2008년 1월에 작성된 글이다.


Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c1c58000
[00000000] *pgd=10360031, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1]
Modules linked in: overflow
CPU: 0
PC is at read_overflow+0x38/0x4c [overflow]
LR is at 0x1
pc : [<bf0000c0>] lr : [<00000001>] Tainted: P
sp : c035baf0 ip : 60000093 fp : c035befc
r10: 00066178 r9 : 00000000 r8 : c1f25000
r7 : 00000c00 r6 : 00002000 r5 : 00000000 r4 : 00000000
r3 : 00000003 r2 : 00000000 r1 : 00001517 r0 : 00000000
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user
Control: 5317F
Table: 11C58000 DAC: 00000015
Process cat (pid: 31, stack limit = 0xc035a258)
Stack: (0xc035baf0 to 0xc035c000)
bae0: 00000000 00000000 00000000 00000000
bb00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bba0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bbc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bbe0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bca0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bcc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bce0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bda0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bdc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bde0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bea0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bec0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bee0: 00000000 00000000 00000000 00000000 c035bf4c c035bf00 c00b24a4 bf000098
bf00: c035bf1c 00000000 00000021 c035bf78 c1fc4920 c035a000 00000000 00000000
bf20: c035bf3c c0dc5b40 00066178 c035bf78 00002000 c0023f88 c035a000 00000000
bf40: c035bf74 c035bf50 c00804c8 c00b2374 c035bfb0 00000000 00000000 00000000
bf60: c0dc5b40 00000003 c035bfa4 c035bf78 c0080ba0 c0080424 00000000 00000000
bf80: c002a4d8 00000000 c035bfa4 bed0aeb4 00002000 00066178 00000000 c035bfa8
bfa0: c0023de0 c0080b6c bed0aeb4 00002000 00000003 00066178 00002000 00000001
bfc0: bed0aeb4 00002000 00066178 00000003 00000001 ffffffff 00000003 00000000
bfe0: 00000003 bed0ab50 00048a24 40119b7c 60000010 00000003 00000000 00000000
Backtrace:
[] (read_overflow+0x0/0x4c [overflow]) from [] (proc_file_read+0x140/0x2bc)
[] (proc_file_read+0x0/0x2bc) from [] (vfs_read+0xb4/0x18c)
[] (vfs_read+0x0/0x18c) from [] (sys_read+0x44/0x70)
r7 = 00000003 r6 = C0DC5B40 r5 = 00000000 r4 = 00000000
[] (sys_read+0x0/0x70) from [] (ret_fast_syscall+0x0/0x2c)
r6 = 00066178 r5 = 00002000 r4 = BED0AEB4
Code: e59f0018 eb40fbc4 e3a00000 e3a03003 (e5803000)
Segmentation fault



1. pgd = c1c58000 값은 fault 시점에서의 current의 mm_struct 구조체 안에 들어있는 pgd 값이다. 즉 pgd가 저장되어 있는 가상 주소이다.

[00000000] *pgd=10360031, *pte=00000000, *ppte=00000000

2. 위의 첫번째 [00000000]은 fault를 일으킨 주소이며
3. *pgd 값은 pgd에 저장되어 있는 값이다.
4. pgd에 저장되어 있는 값을 통해 fault가 난 주소로 reslove한 *pte, *ppte또한 각각이 저장되어 있는 값이다.

Internal error: Oops: 817 [#1]

5. 817은 fsr(Fault Status Register, data sheet 참조)의 값, 즉 error 값이며 #1은 die counter이다. 즉 die가 연속적으로 호출되거나 루프를 돌며 호출될 때 제일 중요하고 의미 있는 것은 첫번째 로그이다. 그것을 구별하도록 해준다.

6. 다음 현재 시스템에 로드되어 있는 모듈의 이름을 print하여 준다.

CPU: 0
PC is at read_overflow+0x38/0x4c [overflow]
LR is at 0x1
pc : [] lr : [<00000001>] Tainted: P
sp : c035baf0 ip : 60000093 fp : c035befc
r10: 00066178 r9 : 00000000 r8 : c1f25000
r7 : 00000c00 r6 : 00002000 r5 : 00000000 r4 : 00000000
r3 : 00000003 r2 : 00000000 r1 : 00001517 r0 : 00000000
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user
Control: 5317F
Table: 11C58000 DAC: 00000015

7. 현재 수행중이었던 CPU를 출력하며
8. fault가 난 시점에서의 register에 저장되어 있던 값들을 출력한다.

9. mode는 드라이버에서 fault를 일으켰음으로 SVC_32 모드이며
10. Segment user가 의미하는 것은 current가 kernel thread가 아닌 user thread에서 일어났다는 것이다. 이것을 아는 코드는 다음과 같다.

get_fs() == get_ds() ? "kernel" : "user");

get_fs()는 current의 thread_info구조체에 들어 있는 addr_limit 값을 가져온다. 반면 get_ds()는 커널의 DS 값을 가져온다.
일반적으로 커널 쓰레드들은 user memory(0x0 ~ 0xc0000000)에 접근할 필요가 없기 때문에 ds 값이

11. 그 위쪽으로는 현재의 stack pointer부터

43 00000088 :
44 88: e1a0c00d mov ip, sp
45 8c: e92dd800 stmdb sp!, {fp, ip, lr, pc}
46 90: e24cb004 sub fp, ip, #4 ; 0x4
47 94: e24ddb01 sub sp, sp, #1024 ; 0x400
48 98: e24b0b01 sub r0, fp, #1024 ; 0x400
49 9c: e3a01b01 mov r1, #1024 ; 0x400
50 a0: e240000c sub r0, r0, #12 ; 0xc
51 a4: ebfffffe bl 0 <__memzero>
52 a8: e59f001c ldr r0, [pc, #28] ; cc <.text+0xcc>
53 ac: ebfffffe bl 0
54 b0: e59f0018 ldr r0, [pc, #24] ; d0 <.text+0xd0>
55 b4: ebfffffe bl 0
56 b8: e3a00000 mov r0, #0 ; 0x0
57 bc: e3a03003 mov r3, #3 ; 0x3
58 c0: e5803000 str r3, [r0]
59 c4: e24bd00c sub sp, fp, #12 ; 0xc
60 c8: e89da800 ldmia sp, {fp, sp, pc}
61 cc: 00000040 .word 0x00000040
62 d0: 0000005c .word 0x0000005c

int read_overflow( char *page, char **start, off_t off,int count,int *eof, void *data_unused )
{
char stack_overflow[OVERFLOW_STACK];
int *die = NULL;
unsigned long fp;
memset(stack_overflow, 0, OVERFLOW_STACK);

printk("!!!!happen overflow!!!!\n");
printk("!!!!wakeup!!!!\n");

asm("mov %0, fp" : "=r" (fp) : : "cc");
*die = 3;

return 0;


12. 이 함수의 call path는 ret_fast_syscall부터 시작되었으며 ret_fast_syscall의 entry point가 위치하는 주소는 0xc0023de0이다.
sys_read 함수가 호출되었으며 이때 다음의 값들은 r6 = 00066178 r5 = 00002000 r4 = BED0AEB4 sys_read의 인수가 아니라 ret_fast_syscall에서 사용된 r5,r6,r7의 값이 sys_read 함수에서 값이 바뀌기 때문에 sys_read 의 entry에 저장되어 있는 값들이다.



13. fault를 일으킨 코드는 test module의 read_overflow 함수의 빨간 색 라인이다. 위의 backtrace 초록색 라인은 fault를 일으킨 코드를 ()로 둘러싸고 있고 그 이전 코드들을 4개 더 보여주고 있다.
다음 backtrace 정보를 보여주고 있다. fault를 일으킨 함수는 read_overflow이며 fault를 일으킨 함수의 가상 주소는 0xbf000088이다. (이는 모듈이기 때문에 그 쪽 주소에 매핑된 것이다.)


저장하는 코드는 아래의 표와 같다. 주의해야 할 것이 이 값들이 마치 함수의 인수라고 착각하는 것이다.
나머지 함수들도 마찬가지로 해석하면 된다.

127033 c0080b5c :
127034 c0080b5c: e1a0c00d mov ip, sp
127035 c0080b60: e92dd870 stmdb sp!, {r4, r5, r6, fp, ip, lr, pc}



14. Control: 5317F 이것은 ARM processor의 control register 정보이다. 이곳에는 MMU, cache등과 관련된 현제 세팅 정보가 들어가 있다.

15. Table: 11C58000 은 Translation Table Base Register의 정보이다.
Translation table base 0 UNP/SBZ RGN P S C 값들이 들어가 있다.

16 DAC: 00000015은 Domain Access Control Register이다.

D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

17. Tip

PC에 저장되어 있는 주소가 현재 fault를 일으킨 명령이다. 그럼 그 명령은 C 소스의 어느 line에 해당할까?


addr2line 0xc30210c0 -e vmlinux -f


2 개의 덧글:

Minchan Kim said...
This comment has been removed by the author.
Anonymous said...

좋은 정보 감사합니다. ^^ 퍼가겠습니다. -Arnold