user_fixup.c 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. /* user_fixup.c: Fix up user copy faults.
  2. *
  3. * Copyright (C) 2004 David S. Miller <davem@redhat.com>
  4. */
  5. #include <linux/compiler.h>
  6. #include <linux/kernel.h>
  7. #include <linux/string.h>
  8. #include <linux/errno.h>
  9. #include <linux/module.h>
  10. #include <asm/uaccess.h>
  11. /* Calculating the exact fault address when using
  12. * block loads and stores can be very complicated.
  13. *
  14. * Instead of trying to be clever and handling all
  15. * of the cases, just fix things up simply here.
  16. */
  17. static unsigned long compute_size(unsigned long start, unsigned long size, unsigned long *offset)
  18. {
  19. unsigned long fault_addr = current_thread_info()->fault_address;
  20. unsigned long end = start + size;
  21. if (fault_addr < start || fault_addr >= end) {
  22. *offset = 0;
  23. } else {
  24. *offset = fault_addr - start;
  25. size = end - fault_addr;
  26. }
  27. return size;
  28. }
  29. unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size)
  30. {
  31. unsigned long offset;
  32. size = compute_size((unsigned long) from, size, &offset);
  33. if (likely(size))
  34. memset(to + offset, 0, size);
  35. return size;
  36. }
  37. EXPORT_SYMBOL(copy_from_user_fixup);
  38. unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size)
  39. {
  40. unsigned long offset;
  41. return compute_size((unsigned long) to, size, &offset);
  42. }
  43. EXPORT_SYMBOL(copy_to_user_fixup);
  44. unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size)
  45. {
  46. unsigned long fault_addr = current_thread_info()->fault_address;
  47. unsigned long start = (unsigned long) to;
  48. unsigned long end = start + size;
  49. if (fault_addr >= start && fault_addr < end)
  50. return end - fault_addr;
  51. start = (unsigned long) from;
  52. end = start + size;
  53. if (fault_addr >= start && fault_addr < end)
  54. return end - fault_addr;
  55. return size;
  56. }
  57. EXPORT_SYMBOL(copy_in_user_fixup);