exception.txt
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:12k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1.      Kernel level exception handling in Linux 2.1.8
  2.   Commentary by Joerg Pommnitz <joerg@raleigh.ibm.com>
  3. When a process runs in kernel mode, it often has to access user 
  4. mode memory whose address has been passed by an untrusted program. 
  5. To protect itself the kernel has to verify this address.
  6. In older versions of Linux this was done with the 
  7. int verify_area(int type, const void * addr, unsigned long size) 
  8. function.
  9. This function verified that the memory area starting at address 
  10. addr and of size size was accessible for the operation specified 
  11. in type (read or write). To do this, verify_read had to look up the 
  12. virtual memory area (vma) that contained the address addr. In the 
  13. normal case (correctly working program), this test was successful. 
  14. It only failed for a few buggy programs. In some kernel profiling
  15. tests, this normally unneeded verification used up a considerable
  16. amount of time.
  17. To overcome this situation, Linus decided to let the virtual memory 
  18. hardware present in every Linux-capable CPU handle this test.
  19. How does this work?
  20. Whenever the kernel tries to access an address that is currently not 
  21. accessible, the CPU generates a page fault exception and calls the 
  22. page fault handler 
  23. void do_page_fault(struct pt_regs *regs, unsigned long error_code)
  24. in arch/i386/mm/fault.c. The parameters on the stack are set up by 
  25. the low level assembly glue in arch/i386/kernel/entry.S. The parameter
  26. regs is a pointer to the saved registers on the stack, error_code 
  27. contains a reason code for the exception.
  28. do_page_fault first obtains the unaccessible address from the CPU 
  29. control register CR2. If the address is within the virtual address 
  30. space of the process, the fault probably occurred, because the page 
  31. was not swapped in, write protected or something similar. However, 
  32. we are interested in the other case: the address is not valid, there 
  33. is no vma that contains this address. In this case, the kernel jumps 
  34. to the bad_area label. 
  35. There it uses the address of the instruction that caused the exception 
  36. (i.e. regs->eip) to find an address where the execution can continue 
  37. (fixup). If this search is successful, the fault handler modifies the 
  38. return address (again regs->eip) and returns. The execution will 
  39. continue at the address in fixup.
  40. Where does fixup point to?
  41. Since we jump to the contents of fixup, fixup obviously points 
  42. to executable code. This code is hidden inside the user access macros. 
  43. I have picked the get_user macro defined in include/asm/uaccess.h as an
  44. example. The definition is somewhat hard to follow, so let's peek at 
  45. the code generated by the preprocessor and the compiler. I selected
  46. the get_user call in drivers/char/console.c for a detailed examination.
  47. The original code in console.c line 1405:
  48.         get_user(c, buf);
  49. The preprocessor output (edited to become somewhat readable):
  50. (
  51.   {        
  52.     long __gu_err = - 14 , __gu_val = 0;        
  53.     const __typeof__(*( (  buf ) )) *__gu_addr = ((buf));        
  54.     if (((((0 + current_set[0])->tss.segment) == 0x18 )  || 
  55.        (((sizeof(*(buf))) <= 0xC0000000UL) && 
  56.        ((unsigned long)(__gu_addr ) <= 0xC0000000UL - (sizeof(*(buf)))))))        
  57.       do {
  58.         __gu_err  = 0;        
  59.         switch ((sizeof(*(buf)))) {        
  60.           case 1: 
  61.             __asm__ __volatile__(        
  62.               "1:      mov" "b" " %2,%" "b" "1n"        
  63.               "2:n"        
  64.               ".section .fixup,"ax"n"        
  65.               "3:      movl %3,%0n"        
  66.               "        xor" "b" " %" "b" "1,%" "b" "1n"        
  67.               "        jmp 2bn"        
  68.               ".section __ex_table,"a"n"        
  69.               "        .align 4n"        
  70.               "        .long 1b,3bn"        
  71.               ".text"        : "=r"(__gu_err), "=q" (__gu_val): "m"((*(struct __large_struct *)
  72.                             (   __gu_addr   )) ), "i"(- 14 ), "0"(  __gu_err  )) ; 
  73.               break;        
  74.           case 2: 
  75.             __asm__ __volatile__(
  76.               "1:      mov" "w" " %2,%" "w" "1n"        
  77.               "2:n"        
  78.               ".section .fixup,"ax"n"        
  79.               "3:      movl %3,%0n"        
  80.               "        xor" "w" " %" "w" "1,%" "w" "1n"        
  81.               "        jmp 2bn"        
  82.               ".section __ex_table,"a"n"        
  83.               "        .align 4n"        
  84.               "        .long 1b,3bn"        
  85.               ".text"        : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
  86.                             (   __gu_addr   )) ), "i"(- 14 ), "0"(  __gu_err  )); 
  87.               break;        
  88.           case 4: 
  89.             __asm__ __volatile__(        
  90.               "1:      mov" "l" " %2,%" "" "1n"        
  91.               "2:n"        
  92.               ".section .fixup,"ax"n"        
  93.               "3:      movl %3,%0n"        
  94.               "        xor" "l" " %" "" "1,%" "" "1n"        
  95.               "        jmp 2bn"        
  96.               ".section __ex_table,"a"n"        
  97.               "        .align 4n"        "        .long 1b,3bn"        
  98.               ".text"        : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
  99.                             (   __gu_addr   )) ), "i"(- 14 ), "0"(__gu_err)); 
  100.               break;        
  101.           default: 
  102.             (__gu_val) = __get_user_bad();        
  103.         }        
  104.       } while (0) ;        
  105.     ((c)) = (__typeof__(*((buf))))__gu_val;        
  106.     __gu_err;
  107.   }
  108. );
  109. WOW! Black GCC/assembly magic. This is impossible to follow, so let's
  110. see what code gcc generates:
  111.  >         xorl %edx,%edx
  112.  >         movl current_set,%eax
  113.  >         cmpl $24,788(%eax)        
  114.  >         je .L1424        
  115.  >         cmpl $-1073741825,64(%esp)
  116.  >         ja .L1423                
  117.  > .L1424:
  118.  >         movl %edx,%eax                        
  119.  >         movl 64(%esp),%ebx
  120.  > #APP
  121.  > 1:      movb (%ebx),%dl                /* this is the actual user access */
  122.  > 2:
  123.  > .section .fixup,"ax"
  124.  > 3:      movl $-14,%eax
  125.  >         xorb %dl,%dl
  126.  >         jmp 2b
  127.  > .section __ex_table,"a"
  128.  >         .align 4
  129.  >         .long 1b,3b
  130.  > .text
  131.  > #NO_APP
  132.  > .L1423:
  133.  >         movzbl %dl,%esi
  134. The optimizer does a good job and gives us something we can actually 
  135. understand. Can we? The actual user access is quite obvious. Thanks 
  136. to the unified address space we can just access the address in user 
  137. memory. But what does the .section stuff do?????
  138. To understand this we have to look at the final kernel:
  139.  > objdump --section-headers vmlinux
  140.  > 
  141.  > vmlinux:     file format elf32-i386
  142.  > 
  143.  > Sections:
  144.  > Idx Name          Size      VMA       LMA       File off  Algn
  145.  >   0 .text         00098f40  c0100000  c0100000  00001000  2**4
  146.  >                   CONTENTS, ALLOC, LOAD, READONLY, CODE
  147.  >   1 .fixup        000016bc  c0198f40  c0198f40  00099f40  2**0
  148.  >                   CONTENTS, ALLOC, LOAD, READONLY, CODE
  149.  >   2 .rodata       0000f127  c019a5fc  c019a5fc  0009b5fc  2**2
  150.  >                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  151.  >   3 __ex_table    000015c0  c01a9724  c01a9724  000aa724  2**2
  152.  >                   CONTENTS, ALLOC, LOAD, READONLY, DATA
  153.  >   4 .data         0000ea58  c01abcf0  c01abcf0  000abcf0  2**4
  154.  >                   CONTENTS, ALLOC, LOAD, DATA
  155.  >   5 .bss          00018e21  c01ba748  c01ba748  000ba748  2**2
  156.  >                   ALLOC
  157.  >   6 .comment      00000ec4  00000000  00000000  000ba748  2**0
  158.  >                   CONTENTS, READONLY
  159.  >   7 .note         00001068  00000ec4  00000ec4  000bb60c  2**0
  160.  >                   CONTENTS, READONLY
  161. There are obviously 2 non standard ELF sections in the generated object
  162. file. But first we want to find out what happened to our code in the
  163. final kernel executable:
  164.  > objdump --disassemble --section=.text vmlinux
  165.  >
  166.  > c017e785 <do_con_write+c1> xorl   %edx,%edx
  167.  > c017e787 <do_con_write+c3> movl   0xc01c7bec,%eax
  168.  > c017e78c <do_con_write+c8> cmpl   $0x18,0x314(%eax)
  169.  > c017e793 <do_con_write+cf> je     c017e79f <do_con_write+db>
  170.  > c017e795 <do_con_write+d1> cmpl   $0xbfffffff,0x40(%esp,1)
  171.  > c017e79d <do_con_write+d9> ja     c017e7a7 <do_con_write+e3>
  172.  > c017e79f <do_con_write+db> movl   %edx,%eax
  173.  > c017e7a1 <do_con_write+dd> movl   0x40(%esp,1),%ebx
  174.  > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
  175.  > c017e7a7 <do_con_write+e3> movzbl %dl,%esi
  176. The whole user memory access is reduced to 10 x86 machine instructions.
  177. The instructions bracketed in the .section directives are no longer
  178. in the normal execution path. They are located in a different section 
  179. of the executable file:
  180.  > objdump --disassemble --section=.fixup vmlinux
  181.  > 
  182.  > c0199ff5 <.fixup+10b5> movl   $0xfffffff2,%eax
  183.  > c0199ffa <.fixup+10ba> xorb   %dl,%dl
  184.  > c0199ffc <.fixup+10bc> jmp    c017e7a7 <do_con_write+e3>
  185. And finally:
  186.  > objdump --full-contents --section=__ex_table vmlinux
  187.  > 
  188.  >  c01aa7c4 93c017c0 e09f19c0 97c017c0 99c017c0  ................
  189.  >  c01aa7d4 f6c217c0 e99f19c0 a5e717c0 f59f19c0  ................
  190.  >  c01aa7e4 080a18c0 01a019c0 0a0a18c0 04a019c0  ................
  191. or in human readable byte order:
  192.  >  c01aa7c4 c017c093 c0199fe0 c017c097 c017c099  ................
  193.  >  c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5  ................
  194.                                ^^^^^^^^^^^^^^^^^
  195.                                this is the interesting part!
  196.  >  c01aa7e4 c0180a08 c019a001 c0180a0a c019a004  ................
  197. What happened? The assembly directives
  198. .section .fixup,"ax"
  199. .section __ex_table,"a"
  200. told the assembler to move the following code to the specified
  201. sections in the ELF object file. So the instructions
  202. 3:      movl $-14,%eax
  203.         xorb %dl,%dl
  204.         jmp 2b
  205. ended up in the .fixup section of the object file and the addresses
  206.         .long 1b,3b
  207. ended up in the __ex_table section of the object file. 1b and 3b
  208. are local labels. The local label 1b (1b stands for next label 1 
  209. backward) is the address of the instruction that might fault, i.e. 
  210. in our case the address of the label 1 is c017e7a5:
  211. the original assembly code: > 1:      movb (%ebx),%dl
  212. and linked in vmlinux     : > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
  213. The local label 3 (backwards again) is the address of the code to handle
  214. the fault, in our case the actual value is c0199ff5:
  215. the original assembly code: > 3:      movl $-14,%eax
  216. and linked in vmlinux     : > c0199ff5 <.fixup+10b5> movl   $0xfffffff2,%eax
  217. The assembly code
  218.  > .section __ex_table,"a"
  219.  >         .align 4
  220.  >         .long 1b,3b
  221. becomes the value pair
  222.  >  c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5  ................
  223.                                ^this is ^this is
  224.                                1b       3b 
  225. c017e7a5,c0199ff5 in the exception table of the kernel.
  226. So, what actually happens if a fault from kernel mode with no suitable
  227. vma occurs?
  228. 1.) access to invalid address:
  229.  > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
  230. 2.) MMU generates exception
  231. 3.) CPU calls do_page_fault
  232. 4.) do page fault calls search_exception_table (regs->eip == c017e7a5);
  233. 5.) search_exception_table looks up the address c017e7a5 in the
  234.     exception table (i.e. the contents of the ELF section __ex_table) 
  235.     and returns the address of the associated fault handle code c0199ff5.
  236. 6.) do_page_fault modifies its own return address to point to the fault 
  237.     handle code and returns.
  238. 7.) execution continues in the fault handling code.
  239. 8.) 8a) EAX becomes -EFAULT (== -14)
  240.     8b) DL  becomes zero (the value we "read" from user space)
  241.     8c) execution continues at local label 2 (address of the
  242.         instruction immediately after the faulting user access).
  243. The steps 8a to 8c in a certain way emulate the faulting instruction.
  244. That's it, mostly. If you look at our example, you might ask why
  245. we set EAX to -EFAULT in the exception handler code. Well, the
  246. get_user macro actually returns a value: 0, if the user access was
  247. successful, -EFAULT on failure. Our original code did not test this
  248. return value, however the inline assembly code in get_user tries to
  249. return -EFAULT. GCC selected EAX to return this value.
  250. NOTE:
  251. Due to the way that the exception table is built and needs to be ordered,
  252. only use exceptions for code in the .text section.  Any other section
  253. will cause the exception table to not be sorted correctly, and the
  254. exceptions will fail.