Code Review - Add exception table kernel fixup

6 views
Skip to first unread message

Davide Libenzi

unread,
Oct 15, 2015, 8:27:50 PM10/15/15
to aka...@googlegroups.com


The following changes since commit fad2f1e22bbe4a7c8d9f5fb9df7ea4f5b563bd9b:

  Allow freeaddrinfo(NULL) (XCC) (2015-10-14 17:14:45 -0400)

are available in the git repository at:

  g...@github.com:dlibenzi/akaros user_memory_access

for you to fetch changes up to 16489ff3b654db956818f6560d98b40ab513ab6a:

  Added new kernel test case (2015-10-15 17:20:49 -0700)

----------------------------------------------------------------
Davide Libenzi (6):
      Added heapsort utility function to the lib/ framework.
      Added safe user memory access APIs
      Plugged the exception handling code
      Fixed a warning in the copy to/from user code.
      Moved trap fixup code in the proper places
      Added new kernel test case

 kern/arch/x86/trap.c            |  33 ++++++++-
 kern/arch/x86/uaccess.h         | 149 ++++++++++++++++++++++++++++++++++++++++
 kern/include/ex_table.h         |   9 +++
 kern/include/sort.h             |   9 +++
 kern/lib/Kbuild                 |   1 +
 kern/lib/sort.c                 |  41 +++++++++++
 kern/src/Kbuild                 |   1 +
 kern/src/ex_table.c             |  62 +++++++++++++++++
 kern/src/init.c                 |   2 +
 kern/src/ktest/Kconfig.postboot |   5 ++
 kern/src/ktest/pb_ktests.c      |  17 +++++
 11 files changed, 326 insertions(+), 3 deletions(-)
 create mode 100644 kern/arch/x86/uaccess.h
 create mode 100644 kern/include/ex_table.h
 create mode 100644 kern/include/sort.h
 create mode 100644 kern/lib/sort.c
 create mode 100644 kern/src/ex_table.c

diff --git a/kern/arch/x86/trap.c b/kern/arch/x86/trap.c
index e31b559..4b65310 100644
--- a/kern/arch/x86/trap.c
+++ b/kern/arch/x86/trap.c
@@ -16,6 +16,7 @@
 #include <syscall.h>
 #include <kdebug.h>
 #include <kmalloc.h>
+#include <ex_table.h>
 #include <arch/mptables.h>
 
 taskstate_t ts;
@@ -29,6 +30,18 @@ pseudodesc_t idt_pd;
 struct irq_handler *irq_handlers[NUM_IRQS];
 spinlock_t irq_handler_wlock = SPINLOCK_INITIALIZER_IRQSAVE;
 
+static bool try_handle_exception_fixup(struct hw_trapframe *hw_tf)
+{
+ uintptr_t fixup_ip;
+
+ if (in_kernel(hw_tf) && ((fixup_ip = get_fixup_ip(hw_tf->tf_rip)) != 0)) {
+ hw_tf->tf_rip = fixup_ip;
+ return true;
+ }
+
+ return false;
+}
+
 const char *x86_trapname(int trapno)
 {
  static const char *const excnames[] = {
@@ -229,6 +242,9 @@ static bool __handle_page_fault(struct hw_trapframe *hw_tf, unsigned long *aux)
  enable_irq();
 
  if (!pcpui->cur_proc) {
+ if (try_handle_exception_fixup(hw_tf))
+ return TRUE;
+
  /* still catch KPFs */
  assert((hw_tf->tf_cs & 3) == 0);
  print_trapframe(hw_tf);
@@ -248,6 +264,9 @@ static bool __handle_page_fault(struct hw_trapframe *hw_tf, unsigned long *aux)
  if (in_kernel(hw_tf))
  pcpui->__lock_checking_enabled++;
  if (err) {
+ if (try_handle_exception_fixup(hw_tf))
+ return TRUE;
+
  if (in_kernel(hw_tf)) {
  print_trapframe(hw_tf);
  backtrace_kframe(hw_tf);
@@ -285,6 +304,8 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
  struct per_cpu_info *pcpui;
  bool handled = TRUE;
  unsigned long aux = 0;
+ uintptr_t fixup_ip;
+
  // Handle processor exceptions.
  switch(hw_tf->tf_trapno) {
  case T_NMI:
@@ -342,7 +363,8 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
  handled = __handle_page_fault(hw_tf, &aux);
  break;
  case T_FPERR:
- handle_fperr(hw_tf);
+ if (!(handled = try_handle_exception_fixup(hw_tf)))
+ handle_fperr(hw_tf);
  break;
  case T_SYSCALL:
  enable_irq();
@@ -362,8 +384,13 @@ static void trap_dispatch(struct hw_trapframe *hw_tf)
  handled = FALSE;
  }
  }
- if (!handled)
- reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
+
+ if (!handled) {
+ if ((fixup_ip = get_fixup_ip(hw_tf->tf_rip)) != 0)
+ hw_tf->tf_rip = fixup_ip;
+ else
+ reflect_unhandled_trap(hw_tf->tf_trapno, hw_tf->tf_err, aux);
+ }
 }
 
 /* Helper.  For now, this copies out the TF to pcpui.  Eventually, we should
diff --git a/kern/arch/x86/uaccess.h b/kern/arch/x86/uaccess.h
new file mode 100644
index 0000000..472d065
--- /dev/null
+++ b/kern/arch/x86/uaccess.h
@@ -0,0 +1,149 @@
+#ifndef ROS_INC_UACCESS_H
+#define ROS_INC_UACCESS_H
+
+#include <ros/errno.h>
+#include <stdint.h>
+
+#define ASM_STAC
+#define ASM_CLAC
+#define __m(x) *(x)
+
+struct fixup {
+ uint64_t insn;
+ uint64_t fixup;
+};
+
+#define _ASM_EXTABLE_INIT() \
+ asm volatile( \
+ " .pushsection \"__ex_table\",\"a\"\n" \
+ " .balign 16\n" \
+ " .popsection\n" \
+ : :)
+
+#define _ASM_EXTABLE(from,to) \
+ " .pushsection \"__ex_table\",\"a\"\n" \
+ " .balign 16\n" \
+ " .quad (" #from ") - .\n" \
+ " .quad (" #to ") - .\n" \
+ " .popsection\n"
+
+#define _ASM_EXTABLE_EX(from,to) \
+ " .pushsection \"__ex_table_ex\",\"a\"\n" \
+ " .balign 16\n" \
+ " .quad (" #from ") - .\n" \
+ " .quad (" #to ") - . + 0x7ffffff0\n" \
+ " .popsection\n"
+
+#define __put_user_asm(x, addr, err, itype, rtype, ltype, errret)       \
+ asm volatile(ASM_STAC "\n" \
+ "1:        mov"itype" %"rtype"1,%2\n" \
+             "2: " ASM_CLAC "\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3:        mov %3,%0\n" \
+ "  jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : "=r"(err) \
+ : ltype(x), "m" (__m(addr)), "i" (errret), "0" (err))
+
+#define __get_user_asm(x, addr, err, itype, rtype, ltype, errret) \
+ asm volatile(ASM_STAC "\n"                                      \
+ "1:        mov"itype" %2,%"rtype"1\n"              \
+ "2: " ASM_CLAC "\n"                                \
+ ".section .fixup,\"ax\"\n" \
+ "3:        mov %3,%0\n"                            \
+ "  xor"itype" %"rtype"1,%"rtype"1\n"               \
+ "  jmp 2b\n"                                       \
+ ".previous\n"                                      \
+ _ASM_EXTABLE(1b, 3b)                               \
+ : "=r" (err), ltype(x)                             \
+ : "m" (__m(addr)), "i" (errret), "0" (err))
+
+#define __user_memcpy(dst, src, count, err, errret) \
+ asm volatile(ASM_STAC "\n" \
+ "1:        rep movsb\n" \
+             "2: " ASM_CLAC "\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3:        mov %4,%0\n" \
+ "  jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : "=r"(err) \
+ : "D" (dst), "S" (src), "c" (count), "i" (errret), "0" (err))
+
+static inline int copy_to_user(void *dst, const void *src, unsigned int count)
+{
+ int err = 0;
+
+ if (!__builtin_constant_p(count))
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ else {
+ switch (count) {
+ case 1:
+ __put_user_asm(*(const uint8_t *) src, (uint8_t *) dst, err, "b",
+   "b", "iq", -EFAULT);
+ break;
+ case 2:
+ __put_user_asm(*(const uint16_t *) src, (uint16_t *) dst, err, "w",
+   "w", "ir", -EFAULT);
+ break;
+ case 4:
+ __put_user_asm(*(const uint32_t *) src, (uint32_t *) dst, err, "l",
+   "k", "ir", -EFAULT);
+ break;
+ case 8:
+ __put_user_asm(*(const uint64_t *) src, (uint64_t *) dst, err, "q",
+   "", "er", -EFAULT);
+ break;
+ default:
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ }
+ }
+
+ return err;
+}
+
+static inline int copy_from_user(void *dst, const void *src,
+ unsigned int count)
+{
+ int err = 0;
+
+ if (!__builtin_constant_p(count))
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ else {
+ switch (count) {
+ case 1:
+ __get_user_asm(*(uint8_t *) dst, (const uint8_t *) src, err, "b",
+   "b", "=q", -EFAULT);
+ break;
+ case 2:
+ __get_user_asm(*(uint16_t *) dst, (const uint16_t *) src, err, "w",
+   "w", "=r", -EFAULT);
+ break;
+ case 4:
+ __get_user_asm(*(uint32_t *) dst, (const uint32_t *) src, err, "l",
+   "k", "=r", -EFAULT);
+ break;
+ case 8:
+ __get_user_asm(*(uint64_t *) dst, (const uint64_t *) src, err, "q",
+   "", "=r", -EFAULT);
+ break;
+ default:
+ __user_memcpy(dst, src, count, err, -EFAULT);
+ }
+ }
+
+ return err;
+}
+
+static inline uintptr_t ex_insn_addr(const struct fixup *x)
+{
+ return (uintptr_t) &x->insn + x->insn;
+}
+
+static inline uintptr_t ex_fixup_addr(const struct fixup *x)
+{
+ return (uintptr_t) &x->fixup + x->fixup;
+}
+
+#endif /* !ROS_INC_UACCESS_H */
diff --git a/kern/include/ex_table.h b/kern/include/ex_table.h
new file mode 100644
index 0000000..8ef2885
--- /dev/null
+++ b/kern/include/ex_table.h
@@ -0,0 +1,9 @@
+#ifndef ROS_INC_EX_TABLE_H
+#define ROS_INC_EX_TABLE_H
+
+#include <stdint.h>
+
+void exception_table_init(void);
+uintptr_t get_fixup_ip(uintptr_t xip);
+
+#endif /* ROS_INC_EX_TABLE_H */
diff --git a/kern/include/sort.h b/kern/include/sort.h
new file mode 100644
index 0000000..11b98b6
--- /dev/null
+++ b/kern/include/sort.h
@@ -0,0 +1,9 @@
+#ifndef ROS_INC_LIB_SORT_H
+#define ROS_INC_LIB_SORT_H
+
+#include <stdio.h>
+
+void sort(void *base, size_t count, size_t size,
+          int (*cmp)(const void *, const void *));
+
+#endif /* ROS_INC_LIB_SORT_H */
diff --git a/kern/lib/Kbuild b/kern/lib/Kbuild
index 1cb408f..aa4a004 100644
--- a/kern/lib/Kbuild
+++ b/kern/lib/Kbuild
@@ -1,2 +1,3 @@
+obj-y += sort.o
 obj-y += zlib_deflate/
 obj-y += zlib_inflate/
diff --git a/kern/lib/sort.c b/kern/lib/sort.c
new file mode 100644
index 0000000..ec448df
--- /dev/null
+++ b/kern/lib/sort.c
@@ -0,0 +1,41 @@
+
+#include <stdio.h>
+
+static void mem_swap(void *a, void *b, size_t size)
+{
+ for (; size > 0; size--, a++, b++) {
+ char tmp = *(char*) a;
+
+ *(char *) a = *(char *) b;
+ *(char *) b = tmp;
+ }
+}
+
+void sort(void *base, size_t count, size_t size,
+          int (*cmp)(const void *, const void *))
+{
+ ssize_t n = count * size, c;
+
+ for (ssize_t i = (count / 2 - 1) * size; i >= 0; i -= size) {
+ for (ssize_t r = i; r * 2 + size < n; r = c) {
+ c = r * 2 + size;
+ if (c < n - size && cmp(base + c, base + c + size) < 0)
+ c += size;
+ if (cmp(base + r, base + c) >= 0)
+ break;
+ mem_swap(base + r, base + c, size);
+ }
+ }
+
+ for (ssize_t i = n - size; i > 0; i -= size) {
+ mem_swap(base, base + i, size);
+ for (ssize_t r = 0; r * 2 + size < i; r = c) {
+ c = r * 2 + size;
+ if (c < i - size && cmp(base + c, base + c + size) < 0)
+ c += size;
+ if (cmp(base + r, base + c) >= 0)
+ break;
+ mem_swap(base + r, base + c, size);
+ }
+ }
+}
diff --git a/kern/src/Kbuild b/kern/src/Kbuild
index 0981c66..d2a9d18 100644
--- a/kern/src/Kbuild
+++ b/kern/src/Kbuild
@@ -26,6 +26,7 @@ obj-y += error.o
 obj-$(CONFIG_ETH_AUDIO) += eth_audio.o
 obj-y += event.o
 obj-y += ext2fs.o
+obj-y += ex_table.o
 obj-y += fdtap.o
 obj-y += find_next_bit.o
 obj-y += find_last_bit.o
diff --git a/kern/src/ex_table.c b/kern/src/ex_table.c
new file mode 100644
index 0000000..8f060a7
--- /dev/null
+++ b/kern/src/ex_table.c
@@ -0,0 +1,62 @@
+
+#include <ex_table.h>
+#include <arch/uaccess.h>
+#include <sort.h>
+
+extern struct fixup __start___ex_table;
+extern struct fixup __stop___ex_table;
+
+static int fixup_cmp(const void *f1, const void *f2)
+{
+ return ((const struct fixup *) f1)->insn <
+ ((const struct fixup *) f2)->insn ? -1: +1;
+}
+
+void exception_table_init(void)
+{
+ struct fixup *first = &__start___ex_table;
+ struct fixup *last = &__stop___ex_table;
+ uint64_t offset = 0;
+
+ for (struct fixup *fx = first; fx < last; fx++) {
+ fx->insn += offset;
+ offset += sizeof(fx->insn);
+ fx->fixup += offset;
+ offset += sizeof(fx->fixup);
+ }
+
+ sort(first, last - first, sizeof(*first), fixup_cmp);
+
+ offset = 0;
+ for (struct fixup *fx = first; fx < last; fx++) {
+ fx->insn -= offset;
+ offset += sizeof(fx->insn);
+ fx->fixup -= offset;
+ offset += sizeof(fx->fixup);
+ }
+
+ /* Avoid undefined __start___ex_table and __stop___ex_table errors when
+ * no code is using the exception table facility.
+ */
+ _ASM_EXTABLE_INIT();
+}
+
+uintptr_t get_fixup_ip(uintptr_t xip)
+{
+ const struct fixup *first = &__start___ex_table;
+ const struct fixup *last = &__stop___ex_table;
+
+ while (first <= last) {
+ const struct fixup *x = first + ((last - first) >> 1);
+ uintptr_t insn = ex_insn_addr(x);
+
+ if (insn < xip)
+ first = x + 1;
+ else if (insn > xip)
+ last = x - 1;
+ else
+ return (uintptr_t) ex_fixup_addr(x);
+ }
+
+ return 0;
+}
diff --git a/kern/src/init.c b/kern/src/init.c
index a795119..ba88555 100644
--- a/kern/src/init.c
+++ b/kern/src/init.c
@@ -29,6 +29,7 @@
 #include <radix.h>
 #include <mm.h>
 #include <frontend.h>
+#include <ex_table.h>
 
 #include <arch/init.h>
 #include <bitmask.h>
@@ -64,6 +65,7 @@ void kernel_init(multiboot_info_t *mboot_info)
  cons_init();
  print_cpuinfo();
 
+ exception_table_init();
  cache_init(); // Determine systems's cache properties
  pmem_init(multiboot_kaddr);
  kmem_cache_init();              // Sets up slab allocator
diff --git a/kern/src/ktest/Kconfig.postboot b/kern/src/ktest/Kconfig.postboot
index 90b61ca..70ef774 100644
--- a/kern/src/ktest/Kconfig.postboot
+++ b/kern/src/ktest/Kconfig.postboot
@@ -238,3 +238,8 @@ config TEST_u16pool
     depends on PB_KTESTS
     bool "u16 pool"
     default n
+
+config TEST_uaccess
+    depends on PB_KTESTS
+    bool "Tests user memory access fault trapping"
+    default y
diff --git a/kern/src/ktest/pb_ktests.c b/kern/src/ktest/pb_ktests.c
index 5dfd003..1866b6e 100644
--- a/kern/src/ktest/pb_ktests.c
+++ b/kern/src/ktest/pb_ktests.c
@@ -6,6 +6,7 @@
 
 #include <arch/mmu.h>
 #include <arch/arch.h>
+#include <arch/uaccess.h>
 #include <bitmask.h>
 #include <smp.h>
 
@@ -2085,6 +2086,21 @@ bool test_u16pool(void)
  return FALSE;
 }
 
+bool test_uaccess(void)
+{
+ char buf[128] = { 0 };
+ char buf2[128] = { 0 };
+
+ if (copy_to_user((void *) UDATA, buf, sizeof(buf)) == 0)
+ return false;
+ if (copy_from_user(buf, (const void *) UDATA, sizeof(buf)) == 0)
+ return false;
+ if (copy_from_user(buf, buf2, sizeof(buf)) != 0)
+ return false;
+
+ return true;
+}
+
 static struct ktest ktests[] = {
 #ifdef CONFIG_X86
  KTEST_REG(ipi_sending,        CONFIG_TEST_ipi_sending),
@@ -2124,6 +2140,7 @@ static struct ktest ktests[] = {
  KTEST_REG(alarm,              CONFIG_TEST_alarm),
  KTEST_REG(kmalloc_incref,     CONFIG_TEST_kmalloc_incref),
  KTEST_REG(u16pool,            CONFIG_TEST_u16pool),
+ KTEST_REG(uaccess,            CONFIG_TEST_uaccess),
 };
 static int num_ktests = sizeof(ktests) / sizeof(struct ktest);
 linker_func_1(register_pb_ktests)

Davide Libenzi

unread,
Oct 15, 2015, 8:36:36 PM10/15/15
to Akaros
Forget about this.
A fix-previous-commit escaped. Will repost.
Reply all
Reply to author
Forward
0 new messages