RHEL4/mm/usercopy.c
<<
>>
Prefs
   1/*
   2 * linux/mm/usercopy.c
   3 *
   4 * (C) Copyright 2003 Ingo Molnar
   5 *
   6 * Generic implementation of all the user-VM access functions, without
   7 * relying on being able to access the VM directly.
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/sched.h>
  12#include <linux/errno.h>
  13#include <linux/mm.h>
  14#include <linux/highmem.h>
  15#include <linux/pagemap.h>
  16#include <linux/smp_lock.h>
  17#include <linux/ptrace.h>
  18#include <linux/interrupt.h>
  19
  20#include <asm/pgtable.h>
  21#include <asm/uaccess.h>
  22#include <asm/atomic_kmap.h>
  23
  24/*
  25 * Get kernel address of the user page and pin it.
  26 */
  27static inline struct page *pin_page(unsigned long addr, int write,
  28                                    pte_t *pte)
  29{
  30        struct mm_struct *mm = current->mm ? : &init_mm;
  31        struct page *page = NULL;
  32        int ret;
  33
  34        if (addr >= current_thread_info()->addr_limit.seg)
  35                return (struct page *)-1UL;
  36        /*
  37         * Do a quick atomic lookup first - this is the fastpath.
  38         */
  39retry:
  40        page = follow_page_pte(mm, addr, write, pte);
  41        if (likely(page != NULL)) {
  42                if (!PageReserved(page))
  43                        get_page(page);
  44                return page;
  45        }
  46        if (pte_present(*pte))
  47                return NULL;
  48        /*
  49         * No luck - bad address or need to fault in the page:
  50         */
  51
  52        /* Release the lock so get_user_pages can sleep */
  53        spin_unlock(&mm->page_table_lock);
  54
  55        /*
  56         * In the context of filemap_copy_from_user(), we are not allowed
  57         * to sleep.  We must fail this usercopy attempt and allow
  58         * filemap_copy_from_user() to recover: drop its atomic kmap and use
  59         * a sleeping kmap instead.
  60         */
  61        if (in_atomic()) {
  62                spin_lock(&mm->page_table_lock);
  63                return NULL;
  64        }
  65
  66        down_read(&mm->mmap_sem);
  67        ret = get_user_pages(current, mm, addr, 1, write, 0, NULL, NULL);
  68        up_read(&mm->mmap_sem);
  69        spin_lock(&mm->page_table_lock);
  70
  71        if (ret <= 0)
  72                return NULL;
  73
  74        /*
  75         * Go try the follow_page again.
  76         */
  77        goto retry;
  78}
  79
  80static inline void unpin_page(struct page *page)
  81{
  82        put_page(page);
  83}
  84
  85/*
  86 * Access another process' address space.
  87 * Source/target buffer must be kernel space,
  88 * Do not walk the page table directly, use get_user_pages
  89 */
  90static int rw_vm(unsigned long addr, void *buf, int len, int write)
  91{
  92        struct mm_struct *mm = current->mm ? : &init_mm;
  93
  94        if (!len)
  95                return 0;
  96
  97        spin_lock(&mm->page_table_lock);
  98
  99        /* ignore errors, just check how much was sucessfully transfered */
 100        while (len) {
 101                struct page *page = NULL;
 102                pte_t pte;
 103                int bytes, offset;
 104                void *maddr;
 105
 106                page = pin_page(addr, write, &pte);
 107                if ((page == (struct page *)-1UL) ||
 108                                        (!page && !pte_present(pte)))
 109                        break;
 110
 111                bytes = len;
 112                offset = addr & (PAGE_SIZE-1);
 113                if (bytes > PAGE_SIZE-offset)
 114                        bytes = PAGE_SIZE-offset;
 115
 116                if (page)
 117                        maddr = kmap_atomic(page, KM_USER_COPY);
 118                else
 119                        /* we will map with user pte
 120                         */
 121                        maddr = kmap_atomic_pte(&pte, KM_USER_COPY);
 122
 123#define HANDLE_TYPE(type) \
 124        case sizeof(type): *(type *)(maddr+offset) = *(type *)(buf); break;
 125
 126                if (write) {
 127                        switch (bytes) {
 128                        HANDLE_TYPE(char);
 129                        HANDLE_TYPE(int);
 130                        HANDLE_TYPE(long long);
 131                        default:
 132                                memcpy(maddr + offset, buf, bytes);
 133                        }
 134                } else {
 135#undef HANDLE_TYPE
 136#define HANDLE_TYPE(type) \
 137        case sizeof(type): *(type *)(buf) = *(type *)(maddr+offset); break;
 138                        switch (bytes) {
 139                        HANDLE_TYPE(char);
 140                        HANDLE_TYPE(int);
 141                        HANDLE_TYPE(long long);
 142                        default:
 143                                memcpy(buf, maddr + offset, bytes);
 144                        }
 145#undef HANDLE_TYPE
 146                }
 147                kunmap_atomic(maddr, KM_USER_COPY);
 148                if (page)
 149                        unpin_page(page);
 150                len -= bytes;
 151                buf += bytes;
 152                addr += bytes;
 153        }
 154        spin_unlock(&mm->page_table_lock);
 155
 156        return len;
 157}
 158
 159static int str_vm(unsigned long addr, void *buf0, int len, int copy)
 160{
 161        struct mm_struct *mm = current->mm ? : &init_mm;
 162        struct page *page;
 163        void *buf = buf0;
 164
 165        if (!len)
 166                return len;
 167
 168        spin_lock(&mm->page_table_lock);
 169
 170        /* ignore errors, just check how much was sucessfully transfered */
 171        while (len) {
 172                int bytes, offset, left, copied;
 173                pte_t pte;
 174                char *maddr;
 175
 176                page = pin_page(addr, copy == 2, &pte);
 177                if ((page == (struct page *)-1UL) ||
 178                                        (!page && !pte_present(pte))) {
 179                        spin_unlock(&mm->page_table_lock);
 180                        return -EFAULT;
 181                }
 182                bytes = len;
 183                offset = addr & (PAGE_SIZE-1);
 184                if (bytes > PAGE_SIZE-offset)
 185                        bytes = PAGE_SIZE-offset;
 186
 187                if (page)
 188                        maddr = kmap_atomic(page, KM_USER_COPY);
 189                else
 190                        /* we will map with user pte
 191                         */
 192                        maddr = kmap_atomic_pte(&pte, KM_USER_COPY);
 193                if (copy == 2) {
 194                        memset(maddr + offset, 0, bytes);
 195                        copied = bytes;
 196                        left = 0;
 197                } else if (copy == 1) {
 198                        left = strncpy_count(buf, maddr + offset, bytes);
 199                        copied = bytes - left;
 200                } else {
 201                        copied = strnlen(maddr + offset, bytes);
 202                        left = bytes - copied;
 203                }
 204                BUG_ON(bytes < 0 || copied < 0);
 205                kunmap_atomic(maddr, KM_USER_COPY);
 206                if (page)
 207                        unpin_page(page);
 208                len -= copied;
 209                buf += copied;
 210                addr += copied;
 211                if (left)
 212                        break;
 213        }
 214        spin_unlock(&mm->page_table_lock);
 215
 216        return len;
 217}
 218
 219/*
 220 * Copies memory from userspace (ptr) into kernelspace (val).
 221 *
 222 * returns # of bytes not copied.
 223 */
 224int get_user_size(unsigned int size, void *val, const void *ptr)
 225{
 226        int ret;
 227
 228        if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
 229                ret = __direct_copy_from_user_inatomic(val, ptr, size);
 230        else
 231                ret = rw_vm((unsigned long)ptr, val, size, 0);
 232        if (ret)
 233                /*
 234                 * Zero the rest:
 235                 */
 236                memset(val + size - ret, 0, ret);
 237        return ret;
 238}
 239
 240/*
 241 * Copies memory from kernelspace (val) into userspace (ptr).
 242 *
 243 * returns # of bytes not copied.
 244 */
 245int put_user_size(unsigned int size, const void *val, void *ptr)
 246{
 247        if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
 248                return __direct_copy_to_user_inatomic(ptr, val, size);
 249        else
 250                return rw_vm((unsigned long)ptr, (void *)val, size, 1);
 251}
 252
 253int copy_str_fromuser_size(unsigned int size, void *val, const void *ptr)
 254{
 255        int copied, left;
 256
 257        if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
 258                left = strncpy_count(val, ptr, size);
 259                copied = size - left;
 260                BUG_ON(copied < 0);
 261
 262                return copied;
 263        }
 264        left = str_vm((unsigned long)ptr, val, size, 1);
 265        if (left < 0)
 266                return left;
 267        copied = size - left;
 268        BUG_ON(copied < 0);
 269
 270        return copied;
 271}
 272
 273int strlen_fromuser_size(unsigned int size, const void *ptr)
 274{
 275        int copied, left;
 276
 277        if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
 278                copied = strnlen(ptr, size) + 1;
 279                BUG_ON(copied < 0);
 280
 281                return copied;
 282        }
 283        left = str_vm((unsigned long)ptr, NULL, size, 0);
 284        if (left < 0)
 285                return 0;
 286        copied = size - left + 1;
 287        BUG_ON(copied < 0);
 288
 289        return copied;
 290}
 291
 292int zero_user_size(unsigned int size, void *ptr)
 293{
 294        int left;
 295
 296        if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
 297                memset(ptr, 0, size);
 298                return 0;
 299        }
 300        left = str_vm((unsigned long)ptr, NULL, size, 2);
 301        if (left < 0)
 302                return size;
 303        return left;
 304}
 305
 306EXPORT_SYMBOL(get_user_size);
 307EXPORT_SYMBOL(put_user_size);
 308EXPORT_SYMBOL(zero_user_size);
 309EXPORT_SYMBOL(copy_str_fromuser_size);
 310EXPORT_SYMBOL(strlen_fromuser_size);
 311