Back to home page

OSCL-LXR

 
 

    


0001 .. SPDX-License-Identifier: GPL-2.0
0002 
0003 ===============================
0004 Kernel level exception handling
0005 ===============================
0006 
0007 Commentary by Joerg Pommnitz <joerg@raleigh.ibm.com>
0008 
0009 When a process runs in kernel mode, it often has to access user
0010 mode memory whose address has been passed by an untrusted program.
0011 To protect itself the kernel has to verify this address.
0012 
0013 In older versions of Linux this was done with the
0014 int verify_area(int type, const void * addr, unsigned long size)
0015 function (which has since been replaced by access_ok()).
0016 
0017 This function verified that the memory area starting at address
0018 'addr' and of size 'size' was accessible for the operation specified
0019 in type (read or write). To do this, verify_read had to look up the
0020 virtual memory area (vma) that contained the address addr. In the
0021 normal case (correctly working program), this test was successful.
0022 It only failed for a few buggy programs. In some kernel profiling
0023 tests, this normally unneeded verification used up a considerable
0024 amount of time.
0025 
0026 To overcome this situation, Linus decided to let the virtual memory
0027 hardware present in every Linux-capable CPU handle this test.
0028 
0029 How does this work?
0030 
0031 Whenever the kernel tries to access an address that is currently not
0032 accessible, the CPU generates a page fault exception and calls the
0033 page fault handler::
0034 
0035   void exc_page_fault(struct pt_regs *regs, unsigned long error_code)
0036 
0037 in arch/x86/mm/fault.c. The parameters on the stack are set up by
0038 the low level assembly glue in arch/x86/entry/entry_32.S. The parameter
0039 regs is a pointer to the saved registers on the stack, error_code
0040 contains a reason code for the exception.
0041 
0042 exc_page_fault() first obtains the inaccessible address from the CPU
0043 control register CR2. If the address is within the virtual address
0044 space of the process, the fault probably occurred, because the page
0045 was not swapped in, write protected or something similar. However,
0046 we are interested in the other case: the address is not valid, there
0047 is no vma that contains this address. In this case, the kernel jumps
0048 to the bad_area label.
0049 
0050 There it uses the address of the instruction that caused the exception
0051 (i.e. regs->eip) to find an address where the execution can continue
0052 (fixup). If this search is successful, the fault handler modifies the
0053 return address (again regs->eip) and returns. The execution will
0054 continue at the address in fixup.
0055 
0056 Where does fixup point to?
0057 
0058 Since we jump to the contents of fixup, fixup obviously points
0059 to executable code. This code is hidden inside the user access macros.
0060 I have picked the get_user() macro defined in arch/x86/include/asm/uaccess.h
0061 as an example. The definition is somewhat hard to follow, so let's peek at
0062 the code generated by the preprocessor and the compiler. I selected
0063 the get_user() call in drivers/char/sysrq.c for a detailed examination.
0064 
0065 The original code in sysrq.c line 587::
0066 
0067         get_user(c, buf);
0068 
0069 The preprocessor output (edited to become somewhat readable)::
0070 
0071   (
0072     {
0073       long __gu_err = - 14 , __gu_val = 0;
0074       const __typeof__(*( (  buf ) )) *__gu_addr = ((buf));
0075       if (((((0 + current_set[0])->tss.segment) == 0x18 )  ||
0076         (((sizeof(*(buf))) <= 0xC0000000UL) &&
0077         ((unsigned long)(__gu_addr ) <= 0xC0000000UL - (sizeof(*(buf)))))))
0078         do {
0079           __gu_err  = 0;
0080           switch ((sizeof(*(buf)))) {
0081             case 1:
0082               __asm__ __volatile__(
0083                 "1:      mov" "b" " %2,%" "b" "1\n"
0084                 "2:\n"
0085                 ".section .fixup,\"ax\"\n"
0086                 "3:      movl %3,%0\n"
0087                 "        xor" "b" " %" "b" "1,%" "b" "1\n"
0088                 "        jmp 2b\n"
0089                 ".section __ex_table,\"a\"\n"
0090                 "        .align 4\n"
0091                 "        .long 1b,3b\n"
0092                 ".text"        : "=r"(__gu_err), "=q" (__gu_val): "m"((*(struct __large_struct *)
0093                               (   __gu_addr   )) ), "i"(- 14 ), "0"(  __gu_err  )) ;
0094                 break;
0095             case 2:
0096               __asm__ __volatile__(
0097                 "1:      mov" "w" " %2,%" "w" "1\n"
0098                 "2:\n"
0099                 ".section .fixup,\"ax\"\n"
0100                 "3:      movl %3,%0\n"
0101                 "        xor" "w" " %" "w" "1,%" "w" "1\n"
0102                 "        jmp 2b\n"
0103                 ".section __ex_table,\"a\"\n"
0104                 "        .align 4\n"
0105                 "        .long 1b,3b\n"
0106                 ".text"        : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
0107                               (   __gu_addr   )) ), "i"(- 14 ), "0"(  __gu_err  ));
0108                 break;
0109             case 4:
0110               __asm__ __volatile__(
0111                 "1:      mov" "l" " %2,%" "" "1\n"
0112                 "2:\n"
0113                 ".section .fixup,\"ax\"\n"
0114                 "3:      movl %3,%0\n"
0115                 "        xor" "l" " %" "" "1,%" "" "1\n"
0116                 "        jmp 2b\n"
0117                 ".section __ex_table,\"a\"\n"
0118                 "        .align 4\n"        "        .long 1b,3b\n"
0119                 ".text"        : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
0120                               (   __gu_addr   )) ), "i"(- 14 ), "0"(__gu_err));
0121                 break;
0122             default:
0123               (__gu_val) = __get_user_bad();
0124           }
0125         } while (0) ;
0126       ((c)) = (__typeof__(*((buf))))__gu_val;
0127       __gu_err;
0128     }
0129   );
0130 
0131 WOW! Black GCC/assembly magic. This is impossible to follow, so let's
0132 see what code gcc generates::
0133 
0134  >         xorl %edx,%edx
0135  >         movl current_set,%eax
0136  >         cmpl $24,788(%eax)
0137  >         je .L1424
0138  >         cmpl $-1073741825,64(%esp)
0139  >         ja .L1423
0140  > .L1424:
0141  >         movl %edx,%eax
0142  >         movl 64(%esp),%ebx
0143  > #APP
0144  > 1:      movb (%ebx),%dl                /* this is the actual user access */
0145  > 2:
0146  > .section .fixup,"ax"
0147  > 3:      movl $-14,%eax
0148  >         xorb %dl,%dl
0149  >         jmp 2b
0150  > .section __ex_table,"a"
0151  >         .align 4
0152  >         .long 1b,3b
0153  > .text
0154  > #NO_APP
0155  > .L1423:
0156  >         movzbl %dl,%esi
0157 
0158 The optimizer does a good job and gives us something we can actually
0159 understand. Can we? The actual user access is quite obvious. Thanks
0160 to the unified address space we can just access the address in user
0161 memory. But what does the .section stuff do?????
0162 
0163 To understand this we have to look at the final kernel::
0164 
0165  > objdump --section-headers vmlinux
0166  >
0167  > vmlinux:     file format elf32-i386
0168  >
0169  > Sections:
0170  > Idx Name          Size      VMA       LMA       File off  Algn
0171  >   0 .text         00098f40  c0100000  c0100000  00001000  2**4
0172  >                   CONTENTS, ALLOC, LOAD, READONLY, CODE
0173  >   1 .fixup        000016bc  c0198f40  c0198f40  00099f40  2**0
0174  >                   CONTENTS, ALLOC, LOAD, READONLY, CODE
0175  >   2 .rodata       0000f127  c019a5fc  c019a5fc  0009b5fc  2**2
0176  >                   CONTENTS, ALLOC, LOAD, READONLY, DATA
0177  >   3 __ex_table    000015c0  c01a9724  c01a9724  000aa724  2**2
0178  >                   CONTENTS, ALLOC, LOAD, READONLY, DATA
0179  >   4 .data         0000ea58  c01abcf0  c01abcf0  000abcf0  2**4
0180  >                   CONTENTS, ALLOC, LOAD, DATA
0181  >   5 .bss          00018e21  c01ba748  c01ba748  000ba748  2**2
0182  >                   ALLOC
0183  >   6 .comment      00000ec4  00000000  00000000  000ba748  2**0
0184  >                   CONTENTS, READONLY
0185  >   7 .note         00001068  00000ec4  00000ec4  000bb60c  2**0
0186  >                   CONTENTS, READONLY
0187 
0188 There are obviously 2 non standard ELF sections in the generated object
0189 file. But first we want to find out what happened to our code in the
0190 final kernel executable::
0191 
0192  > objdump --disassemble --section=.text vmlinux
0193  >
0194  > c017e785 <do_con_write+c1> xorl   %edx,%edx
0195  > c017e787 <do_con_write+c3> movl   0xc01c7bec,%eax
0196  > c017e78c <do_con_write+c8> cmpl   $0x18,0x314(%eax)
0197  > c017e793 <do_con_write+cf> je     c017e79f <do_con_write+db>
0198  > c017e795 <do_con_write+d1> cmpl   $0xbfffffff,0x40(%esp,1)
0199  > c017e79d <do_con_write+d9> ja     c017e7a7 <do_con_write+e3>
0200  > c017e79f <do_con_write+db> movl   %edx,%eax
0201  > c017e7a1 <do_con_write+dd> movl   0x40(%esp,1),%ebx
0202  > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
0203  > c017e7a7 <do_con_write+e3> movzbl %dl,%esi
0204 
0205 The whole user memory access is reduced to 10 x86 machine instructions.
0206 The instructions bracketed in the .section directives are no longer
0207 in the normal execution path. They are located in a different section
0208 of the executable file::
0209 
0210  > objdump --disassemble --section=.fixup vmlinux
0211  >
0212  > c0199ff5 <.fixup+10b5> movl   $0xfffffff2,%eax
0213  > c0199ffa <.fixup+10ba> xorb   %dl,%dl
0214  > c0199ffc <.fixup+10bc> jmp    c017e7a7 <do_con_write+e3>
0215 
0216 And finally::
0217 
0218  > objdump --full-contents --section=__ex_table vmlinux
0219  >
0220  >  c01aa7c4 93c017c0 e09f19c0 97c017c0 99c017c0  ................
0221  >  c01aa7d4 f6c217c0 e99f19c0 a5e717c0 f59f19c0  ................
0222  >  c01aa7e4 080a18c0 01a019c0 0a0a18c0 04a019c0  ................
0223 
0224 or in human readable byte order::
0225 
0226  >  c01aa7c4 c017c093 c0199fe0 c017c097 c017c099  ................
0227  >  c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5  ................
0228                                ^^^^^^^^^^^^^^^^^
0229                                this is the interesting part!
0230  >  c01aa7e4 c0180a08 c019a001 c0180a0a c019a004  ................
0231 
0232 What happened? The assembly directives::
0233 
0234   .section .fixup,"ax"
0235   .section __ex_table,"a"
0236 
0237 told the assembler to move the following code to the specified
0238 sections in the ELF object file. So the instructions::
0239 
0240   3:      movl $-14,%eax
0241           xorb %dl,%dl
0242           jmp 2b
0243 
0244 ended up in the .fixup section of the object file and the addresses::
0245 
0246         .long 1b,3b
0247 
0248 ended up in the __ex_table section of the object file. 1b and 3b
0249 are local labels. The local label 1b (1b stands for next label 1
0250 backward) is the address of the instruction that might fault, i.e.
0251 in our case the address of the label 1 is c017e7a5:
0252 the original assembly code: > 1:      movb (%ebx),%dl
0253 and linked in vmlinux     : > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
0254 
0255 The local label 3 (backwards again) is the address of the code to handle
0256 the fault, in our case the actual value is c0199ff5:
0257 the original assembly code: > 3:      movl $-14,%eax
0258 and linked in vmlinux     : > c0199ff5 <.fixup+10b5> movl   $0xfffffff2,%eax
0259 
0260 If the fixup was able to handle the exception, control flow may be returned
0261 to the instruction after the one that triggered the fault, ie. local label 2b.
0262 
0263 The assembly code::
0264 
0265  > .section __ex_table,"a"
0266  >         .align 4
0267  >         .long 1b,3b
0268 
0269 becomes the value pair::
0270 
0271  >  c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5  ................
0272                                ^this is ^this is
0273                                1b       3b
0274 
0275 c017e7a5,c0199ff5 in the exception table of the kernel.
0276 
0277 So, what actually happens if a fault from kernel mode with no suitable
0278 vma occurs?
0279 
0280 #. access to invalid address::
0281 
0282     > c017e7a5 <do_con_write+e1> movb   (%ebx),%dl
0283 #. MMU generates exception
0284 #. CPU calls exc_page_fault()
0285 #. exc_page_fault() calls do_user_addr_fault()
0286 #. do_user_addr_fault() calls kernelmode_fixup_or_oops()
0287 #. kernelmode_fixup_or_oops() calls fixup_exception() (regs->eip == c017e7a5);
0288 #. fixup_exception() calls search_exception_tables()
0289 #. search_exception_tables() looks up the address c017e7a5 in the
0290    exception table (i.e. the contents of the ELF section __ex_table)
0291    and returns the address of the associated fault handle code c0199ff5.
0292 #. fixup_exception() modifies its own return address to point to the fault
0293    handle code and returns.
0294 #. execution continues in the fault handling code.
0295 #. a) EAX becomes -EFAULT (== -14)
0296    b) DL  becomes zero (the value we "read" from user space)
0297    c) execution continues at local label 2 (address of the
0298       instruction immediately after the faulting user access).
0299 
0300 The steps 8a to 8c in a certain way emulate the faulting instruction.
0301 
0302 That's it, mostly. If you look at our example, you might ask why
0303 we set EAX to -EFAULT in the exception handler code. Well, the
0304 get_user() macro actually returns a value: 0, if the user access was
0305 successful, -EFAULT on failure. Our original code did not test this
0306 return value, however the inline assembly code in get_user() tries to
0307 return -EFAULT. GCC selected EAX to return this value.
0308 
0309 NOTE:
0310 Due to the way that the exception table is built and needs to be ordered,
0311 only use exceptions for code in the .text section.  Any other section
0312 will cause the exception table to not be sorted correctly, and the
0313 exceptions will fail.
0314 
0315 Things changed when 64-bit support was added to x86 Linux. Rather than
0316 double the size of the exception table by expanding the two entries
0317 from 32-bits to 64 bits, a clever trick was used to store addresses
0318 as relative offsets from the table itself. The assembly code changed
0319 from::
0320 
0321     .long 1b,3b
0322   to:
0323           .long (from) - .
0324           .long (to) - .
0325 
0326 and the C-code that uses these values converts back to absolute addresses
0327 like this::
0328 
0329         ex_insn_addr(const struct exception_table_entry *x)
0330         {
0331                 return (unsigned long)&x->insn + x->insn;
0332         }
0333 
0334 In v4.6 the exception table entry was expanded with a new field "handler".
0335 This is also 32-bits wide and contains a third relative function
0336 pointer which points to one of:
0337 
0338 1) ``int ex_handler_default(const struct exception_table_entry *fixup)``
0339      This is legacy case that just jumps to the fixup code
0340 
0341 2) ``int ex_handler_fault(const struct exception_table_entry *fixup)``
0342      This case provides the fault number of the trap that occurred at
0343      entry->insn. It is used to distinguish page faults from machine
0344      check.
0345 
0346 More functions can easily be added.
0347 
0348 CONFIG_BUILDTIME_TABLE_SORT allows the __ex_table section to be sorted post
0349 link of the kernel image, via a host utility scripts/sorttable. It will set the
0350 symbol main_extable_sort_needed to 0, avoiding sorting the __ex_table section
0351 at boot time. With the exception table sorted, at runtime when an exception
0352 occurs we can quickly lookup the __ex_table entry via binary search.
0353 
0354 This is not just a boot time optimization, some architectures require this
0355 table to be sorted in order to handle exceptions relatively early in the boot
0356 process. For example, i386 makes use of this form of exception handling before
0357 paging support is even enabled!