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