Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

16312
2027
233849


[리눅스커널] 커널 크래시: Data Abort @usb_copy_descriptors [Kernel]Crash: Troubleshooting

소개

이번 시간에는 커널 패닉을 디버깅하는 과정을 소개합니다.

1차 분석

콜스택을 TRACE32로 잡아 보니 Data abort로 커널 패닉이 발생했습니다.

-000|el1_da(asm)
 -->|exception
-001|usb_copy_descriptors(src = 0xFFFFFFF50D806080)
-002|configfs_composite_bind(gadget = 0xFFFFFFF4C4F16298, gdriver = 0xFFFFFFF5B2943B08)
-003|udc_bind_to_driver(udc = 0xFFFFFFF5B67BD000, driver = 0xFFFFFFF5B2943B08)
-004|usb_udc_attach_driver(name = 0xFFFFFFF57B6C3F80, driver = 0xFFFFFFF5B2943B08)
-005|schedule_work(inline)
-005|gadget_dev_desc_UDC_store(item = 0xFFFFFFF5B2943800, ?, len = 12)
-006|configfs_write_file(file = 0xFFFFFFF4D93A7F00, buf = 0x0000007F961D31D1, ?, ppos = 0xFFFFFFF5B9363EB
-007|__vfs_write(file = 0xFFFFFFF4D93A7F00, ?, ?, pos = 0xFFFFFFF5B9363EB0)
-008|vfs_write(file = 0xFFFFFFF4D93A7F00, buf = 0x0000007F961D31D1, ?, pos = 0xFFFFFFF5B9363EB0)
-009|sys_write(?, ?, ?)
-010|el0_svc_naked(asm)
 -->|exception
-011|NUX:0x7F9741D688(asm)
 ---|end of frame
 
Data Abort가 발생한 인스트럭션은 'ldrb w2,[x2]' 인데 x2는 0x736563697665642F입니다.
'0x736563697665642F'이라? 주소의 형태만 봐도 MMU가 처리할 수 없는 주소로 보입니다.

 ______________addr/line|code___________|label____|mnemonic________________|comment
   NSX:FFFFFF84CDC306B8|91000421                  add     x1,x1,#0x1       ; x1,x1,#1
   NSX:FFFFFF84CDC306BC|8B010EA2                  add     x2,x21,x1,lsl #0x3   ; x2,src,x1,lsl #3
   NSX:FFFFFF84CDC306C0|F85F8042                  ldur    x2,[x2,#-0x8]    ; x2,[x2,#-8]
   NSX:FFFFFF84CDC306C4|B4000082                  cbz     x2,0xFFFFFF84CDC306D4
___NSX:FFFFFF84CDC306C8|39400042__________________ldrb____w2,[x2]

Data Abort가 발생한 소스 코드의 위치는 어딜까요?

y.l.line 0xFFFFFF84CDC306C8

linux-next\drivers\usb\gadget\config.c|\136--136   

이번에는 'v.l %l %t' 명령어로 지역 변수를 어느 레지스터가 저장했는지 확인해볼까요?

usb_copy_descriptors(
    (register struct usb_descriptor_header * *) [X21] src = 0xFFFFFFF50D806080)
  (register unsigned int) [X19] n_desc = 0
  
x21은 0xFFFFFFF50D806080입니다.

0xFFFFFFF50D806080의 출처는 0xFFFFFFF5551DD800입니다.  

  (struct usb_function *) [-] (struct usb_function*)0xFFFFFFF5551DD800 = 0xFFFFFFF5551DD800 -> (
    (char *) [D:0xFFFFFFF5551DD800] name = 0xFFFFFF84CEF20533 -> "mtp",
    (int) [D:0xFFFFFFF5551DD808] intf_id = 0,
    (struct usb_gadget_strings * *) [D:0xFFFFFFF5551DD810] strings = 0xFFFFFF84CFEE3138,
    (struct usb_descriptor_header * *) [D:0xFFFFFFF5551DD818] fs_descriptors = 0xFFFFFFF50D806080 -> 0x1 -> (  //<<--
      (__u8) [D:0x1] bLength = 0,
      (__u8) [D:0x2] bDescriptorType = 0),
    (struct usb_descriptor_header * *) [D:0xFFFFFFF5551DD820] hs_descriptors = 0xFFFFFFF50D806280 -> 0x1 -> ( //<<--
      (__u8) [D:0x1] bLength = 0,
      (__u8) [D:0x2] bDescriptorType = 0),
    (struct usb_descriptor_header * *) [D:0xFFFFFFF5551DD828] ss_descriptors = 0xFFFFFFF50D806F00 -> 0x0 -> NULL //<<--

그런데 코어 덤프에서는 otg_desc[0]이 NULL입니다.

  (static struct usb_descriptor_header * [2]) otg_desc = (
    [0] = 0x0 =  -> NULL,
    [1] = 0x0 =  -> NULL)
(where)
1764static void configfs_composite_unbind(struct usb_gadget *gadget)
1765{
1766 struct usb_composite_dev *cdev;
1767 struct gadget_info *gi;
1768
1769 /* the gi->lock is hold by the caller */
1770
1771 cdev = get_gadget_data(gadget);
1772 gi = container_of(cdev, struct gadget_info, cdev);
1773
1774 kfree(otg_desc[0]);
1775 otg_desc[0] = NULL; //<<--

Solution Patch

아래와 같이 코드를 수정하면 Data Abort를 방지할 수 있습니다.

diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index ec94fbb..be06b39 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1570,7 +1570,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
                                goto err_purge_funcs;
                        }
-                       if (f->multi_config_support) {
+                       if (f->multi_config_support && !otg_desc[0]) {
                                if (f->fs_descriptors)
                                        f->fs_descriptors =
                                                usb_copy_descriptors(f->fs_descriptors);

덧글

댓글 입력 영역