Quite interesting patch was released under the following url.

https://source.codeaurora.org/quic/la/kernel/msm-3.18/commit/?h=rel/msm-3.18&id=a0039b1e721b7b3ee1cbe7f7f9d44d451ac74543


The detailed patch is to initialize the stack array with a different way as below.

-------------------------------------------------------------------------------------

usb : dwc3: Initialize kernel stack variables properly

If kernel stack variables are not initialized properly,

there  is a chance of kernel information disclosure.

So, initialize kernel stack variables with null characters.


-rw-r--r-- drivers/usb/dwc3/debugfs.c 10

1 files changed, 4 insertions, 6 deletions

diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c

index ce7cd96..fb252ec 100644

--- a/drivers/usb/dwc3/debugfs.c

+++ b/drivers/usb/dwc3/debugfs.c

@@ -402,7 +402,7 @@ static ssize_t dwc3_mode_write(struct file *file,

  struct dwc3 *dwc = s->private;

  unsigned long flags;

  u32 mode = 0;

- char buf[32];

+ char buf[32] = {0};


  if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))

  return -EFAULT;

@@ -482,7 +482,7 @@ static ssize_t dwc3_testmode_write(struct file *file,

  struct dwc3 *dwc = s->private;

  unsigned long flags;

  u32 testmode = 0;

- char buf[32];

+ char buf[32] = {0};


  if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))

  return -EFAULT;

@@ -589,7 +589,7 @@ static ssize_t dwc3_link_state_write(struct file *file,

  struct dwc3 *dwc = s->private;

  unsigned long flags;

  enum dwc3_link_state state = 0;

- char buf[32];

+ char buf[32] = {0};


  if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))

  return -EFAULT;

@@ -630,12 +630,10 @@ static ssize_t dwc3_store_ep_num(struct file *file, const char __user *ubuf,

 {

  struct seq_file *s = file->private_data;

  struct dwc3 *dwc = s->private;

- char kbuf[10];

+ char kbuf[10] = {0};

  unsigned int num, dir, temp;

  unsigned long flags;


- memset(kbuf, 0, 10);

-

  if (copy_from_user(kbuf, ubuf, count > 10 ? 10 : count))

  return -EFAULT;

-------------------------------------------------------------------------------------


In order to find out how the assemble code is updated after compling the patch-set, let me walk through more within dwc3_mode_write() .

The interesting debug information inside updated assemble code after "char buf[32] = {0};" is declared is that memset is appended by ARM-GCC compiler

[1]: R1 is updated as 0x0.

[2]: R2 is updated as 32 in decimal format.

[3]: R0 is belonging to stack memory area.

[4]: Call to memset is made.

memset(R0: buf, R1: 0x0, R2: 32)


[After]

  static ssize_t dwc3_mode_write(struct file *file,

                  const char __user *ubuf, size_t count, loff_t *ppos)

  {

 c0629d9c:       e1a0c00d        mov     ip, sp

 c0629da0:       e92ddbf0        push    {r4, r5, r6, r7, r8, r9, fp, ip, lr, pc}

 c0629da4:       e24cb004        sub     fp, ip, #4

 c0629da8:       e24dd028        sub     sp, sp, #40     ; 0x28

 c0629dac:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)

 c0629db0:       ebe7958c        bl      c000f3e8 <__gnu_mcount_nc>

 c0629db4:       e59f7130        ldr     r7, [pc, #304]  ; c0629eec <dwc3_mode_write+0x150>

 c0629db8:       e1a04001        mov     r4, r1

 c0629dbc:       e1a05002        mov     r5, r2

 c0629dc0:       e3a01000        mov     r1, #0  //<<--[1]

 c0629dc4:       e3a02020        mov     r2, #32  //<<--[2]

 c0629dc8:       e5973000        ldr     r3, [r7]

 c0629dcc:       e50b3028        str     r3, [fp, #-40]  ; 0xffffffd8

 c0629dd0:       e59030bc        ldr     r3, [r0, #188]  ; 0xbc

 c0629dd4:       e24b0048        sub     r0, fp, #72     ; 0x48  //<<--[3]

 c0629dd8:       e5936060        ldr     r6, [r3, #96]   ; 0x60    

 c0629ddc:       ebf2ec6f        bl      c02e4fa0 <memset>        //<<--[4]  

 c0629de0:       e1a0200d        mov     r2, sp                    

 c0629de4:       e3c21d7f        bic     r1, r2, #8128   ; 0x1fc0


[Before]

[1]: Assemble code before the patch is applied, the stack memory location mapped to buf[32] is saved into R0 without initilization.

 static ssize_t dwc3_mode_write(struct file *file,

                 const char __user *ubuf, size_t count, loff_t *ppos)

 {

 c0629d9c:       e1a0c00d        mov     ip, sp

 c0629da0:       e92ddbf0        push    {r4, r5, r6, r7, r8, r9, fp, ip, lr, pc}

 c0629da4:       e24cb004        sub     fp, ip, #4

 c0629da8:       e24dd028        sub     sp, sp, #40     ; 0x28

 c0629dac:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)

 c0629db0:       ebe7958c        bl      c000f3e8 <__gnu_mcount_nc>

 c0629db4:       e1a05002        mov     r5, r2

//.. skip ,,

 c0629dfc:       1a000005        bne     c0629e18 <dwc3_mode_write+0x7c>

 c0629e00:       e24b0048        sub     r0, fp, #72     ; 0x48  //<<--[1]

 c0629e04:       e1a02003        mov     r2, r3

 c0629e08:       ebf2e301        bl      c02e2a14 <__copy_from_user>

 c0629e0c:       e3500000        cmp     r0, #0


Let me make it a rule to initialize the stack array like char kbuf[10] = {0}; instead of char kbuf[10];

[1]: SP address is now FFFFFFEB67BE7A40

[2]: With "str     x21,[SP,#-0x30]!" instruction, the stack is pushed up to 0x30 byte.

[3]: X19 and X20 is pushed into stack.

[4]: X29 and X30 is pushed into stack.


15 collision = find_worker_executing_work(pool, work);

16 if (unlikely(collision)) {

17 move_linked_works(work, &collision->scheduled, NULL);

18 return;

19 }


ZSX:FFFFFF8B40CE71B8|F81D0FF5  set_next_entity:  str     x21,[SP,#-0x30]!   ; x21,[SP,#-48]!  //<<--[2]

ZSX:FFFFFF8B40CE71BC|A9014FF4                    stp     x20,x19,[SP,#0x10]   ; x20,x19,[SP,#16] //<<--[3]

ZSX:FFFFFF8B40CE71C0|A9027BFD                    stp     x29,x30,[SP,#0x20]   ; x29,x30,[SP,#32] //<<--[4]

ZSX:FFFFFF8B40CE71C4|910083FD                    add     x29,SP,#0x20     ; x29,SP,#32

ZSX:FFFFFF8B40CE71C8|AA0103F3____________________mov_____x19,x1


_____________address|_data____________________|value_____________|symbol

ZSD:FFFFFFEB67BE79F8| 80 7B AB 65 EB FF FF FF  0xFFFFFFEB65AB7B80

ZSD:FFFFFFEB67BE7A00| 28 50 5D 79 EB FF FF FF  0xFFFFFFEB795D5028

ZSD:FFFFFFEB67BE7A08| 00 EA 40 68 EB FF FF FF  0xFFFFFFEB6840EA00

ZSD:FFFFFFEB67BE7A10| 00 00 00 00 00 00 00 00  0x0  //<<--[2]

ZSD:FFFFFFEB67BE7A18| 00 00 00 00 00 00 00 00  0x0

ZSD:FFFFFFEB67BE7A20| 20 00 00 00 00 00 00 00  0x20

ZSD:FFFFFFEB67BE7A28| 19 00 00 00 00 00 00 00  0x19  //<<--[3]

ZSD:FFFFFFEB67BE7A30| 29 00 00 00 00 00 00 00  0x29

ZSD:FFFFFFEB67BE7A38| 80 3A CE 40 8B FF FF FF  0xFFFFFF8B40CE3A80 \\vmlinux\fair\pick_next_task_fair+0x4A8 //<<--[4]

ZSD:FFFFFFEB67BE7A40| A0 7A BE 67 EB FF FF FF  0xFFFFFFEB67BE7AA0 //<<--[1]

tst 명령어를 실행하면 ARM CPSR 레지스터가 어떻게 변경될까?


tst 명령어는 연산자와 비연산자 사이 AND 비트 연산을 수행합니다.


AND 비트 연산 결과에 따라 CPSR 레지스터 Z 비트는 다음과 같이 변경됩니다.

Z: 0 ( AND 비트 연산 결과가 1인 경우)

Z: 1 ( AND 비트 연산 결과가 0인 경우)


tst 명령어가 위와 같이 동작하는지 증명하기 위해 T32 디버거를 실행해 보겠습니다.

다음 화면은 tst 명령어를 실행하기 직전입니다. 

NSR:80107E6C|ret_to_user_from_irq:        ldr     r2,[r9,#0x8]

NSR:80107E70|                             cmp     r2,#0x7F000000   ; r2,#2130706432

NSR:80107E74|                             blne    0x8010B5FC       ; addr_limit_check_failed

NSR:80107E78|                             ldr     r1,[r9]

NSR:80107E7C|_____________________________tst_____r1,#0x0F_________;_r1,#15

NSR:80107E80|                             bne     0x80107E48       ; slow_work_pending

NSR:80107E84|no_work_pending:             bl      0x801E9CE8       ; trace_hardirqs_on


N _  R0          0  R8          0   

Z Z  R1          2  R9          0

C _  R2          0  R10         0

V _  R3          0  R11         0

Q _  R4   00020000  R12         0

     R5          0  R13  C000D000

0 _  R6          0  R14         0

1 _  R7          0  PC   80107E7C

2 _  SPSR       10  CPSR 400001D3


R1가 2(이진수 10)이니 0xF와 AND 비트 연산을 하면 1이 될 것입니다.

1111 (0xF)

  10 (2: R1) 

------

   1 (연산 결과)


다음은 "tst r1,#0x0F" 명령어를 실행한 후 결과입니다.

NSR:80107E6C|ret_to_user_from_irq:        ldr     r2,[r9,#0x8]

NSR:80107E70|                             cmp     r2,#0x7F000000   ; r2,#2130706432

NSR:80107E74|                             blne    0x8010B5FC       ; addr_limit_check_failed

NSR:80107E78|                             ldr     r1,[r9]

NSR:80107E7C|                             tst     r1,#0x0F         ; r1,#15

NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending

NSR:80107E84|no_work_pending:             bl      0x801E9CE8       ; trace_hardirqs_on


N _  R0          0  R8          0   

Z _  R1          2  R9          0

C _  R2          0  R10         0

V _  R3          0  R11         0

Q _  R4   00020000  R12         0

     R5          0  R13  C000D000

0 _  R6          0  R14         0

1 _  R7          0  PC   80107E80

2 _  SPSR       10  CPSR     01D3


여기서 눈을 크게 뜨고 봐야 할 중요한 정보는 ARM CPSR 레지스터입니다.

CPSR 레지스터가 0x1D3으로 변경됐습니다. CPSR 레지스터 Z 비트가 0이란 이야기입니다.


bne 명령어는 'branch not equal'이란 뜻입니다. Z가 0이면 브랜치하겠다는 의미입니다.

bne 0x80107E48 ;slow_work_pending


CPSR 레지스터 Z 비트가 0이니 slow_work_pending 레이블로 브랜치 할 것입니다.

NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending


이 결과를 예상하면서 위 "bne 0x80107E48 ;slow_work_pending" 코드를 실행하니 

slow_work_pending 레이블로 브랜치했습니다.

NSR:80107E48|E1A0000D__slow_work_pending:__cpy_____r0,r13

NSR:80107E4C|E1A02008                      cpy     r2,r8

NSR:80107E50|EB000D82                      bl      0x8010B460       ; do_work_pending

NSR:80107E54|E3500000                      cmp     r0,#0x0          ; r0,#0

NSR:80107E58|0A000009                      beq     0x80107E84       ; no_work_pending

NSR:80107E5C|B3A07000                      movlt   r7,#0x0          ; r7,#0

NSR:80107E60|E89D007F                      ldm     r13,{r0-r6}

NSR:80107E64|EA000032                      b       0x80107F34       ; local_restart


이번엔 r1 레지스터가 0x1000 일 때 tst 명령어가 어떤 동작을 하는지 확인해봅시다.

NSR:80107E6C|ret_to_user_from_irq:        ldr     r2,[r9,#0x8]

NSR:80107E70|                             cmp     r2,#0x7F000000   ; r2,#2130706432

NSR:80107E74|                             blne    0x8010B5FC       ; addr_limit_check_failed

NSR:80107E78|                             ldr     r1,[r9]

NSR:80107E7C|_____________________________tst_____r1,#0x0F_________;_r1,#15

NSR:80107E80|                             bne     0x80107E48       ; slow_work_pending

NSR:80107E84|no_work_pending:             bl      0x801E9CE8       ; trace_hardirqs_on


N _  R0          0  R8          0   

Z _  R1       1000  R9          0

C _  R2          0  R10         0

V _  R3          0  R11         0

Q _  R4   00020000  R12         0

     R5          0  R13  C000D000

0 _  R6          0  R14         0

1 _  R7          0  PC   80107E7C

2 _  SPSR       10  CPSR     01D3


위 명령어를 실행하기 전 CPSR 레지스터는 0x1D3이고 R1이 0x1000이란 사실을 기억합시다.

0xF는 이진수로 1111이고 R1가 저장하고 있는 0x1000은 이진수로 1111_0000_0000_0000 일 것입니다.


따라서 AND 비트 연산 결과는 0일 것입니다.

1111_0000_0000_0000  ( 0x1000: R1)

               1111  ( 0xF)

--------------------------------------------- AND 연산

            0    (결과)


이 사실을 염두해두고 "tst r1,#0x0F" 명령어를 T32로 실행하겠습니다.

결과 화면은 다음과 같습니다.


NSR:80107E6C|ret_to_user_from_irq:        ldr     r2,[r9,#0x8]

NSR:80107E70|                             cmp     r2,#0x7F000000   ; r2,#2130706432

NSR:80107E74|                             blne    0x8010B5FC       ; addr_limit_check_failed

NSR:80107E78|                             ldr     r1,[r9]

NSR:80107E7C|                             tst     r1,#0x0F         ; r1,#15

NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending

NSR:80107E84|no_work_pending:             bl      0x801E9CE8       ; trace_hardirqs_on


[레지스터 세트]

N _  R0          0  R8          0   

Z Z  R1       1000  R9          0

C _  R2          0  R10         0

V _  R3          0  R11         0

Q _  R4   00020000  R12         0

     R5          0  R13  C000D000

0 _  R6          0  R14         0

1 _  R7          0  PC   80107E80

2 _  SPSR       10  CPSR 400001D3


CPSR 레지스터가 400001D3 이고 Z 비트가 1이 됐습니다.

레지스터 세트에서 붉은색으로 표시된 부분을 눈으로 따라가 보시기 바랍니다.

N _  R0          0  R8          0   

Z Z  R1       1000  R9          0


Z 필드가 켜져 있습니다.


Z 비트가 1인데 다음 bne 명령어를 실행하면 어떻게 실행할까요? 

NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending

NSR:80107E84|no_work_pending:             bl      0x801E9CE8       ; trace_hardirqs_on


당연히 slow_work_pending 레이블로 브랜치하지 않고 80107E84 주소로 브랜치할 것입니다.

NSR:80107E6C|ret_to_user_from_irq:        ldr     r2,[r9,#0x8]

NSR:80107E70|                             cmp     r2,#0x7F000000   ; r2,#2130706432

NSR:80107E74|                             blne    0x8010B5FC       ; addr_limit_check_failed

NSR:80107E78|                             ldr     r1,[r9]

NSR:80107E7C|                             tst     r1,#0x0F         ; r1,#15

NSR:80107E80|                             bne     0x80107E48       ; slow_work_pending

NSR:80107E84|no_work_pending:_____________bl______0x801E9CE8_______;_trace_hardirqs_on

+ Recent posts