linux-old/mm/mprotect.c
<<
>>
Prefs
   1/*
   2 *      linux/mm/mprotect.c
   3 *
   4 *  (C) Copyright 1994 Linus Torvalds
   5 */
   6#include <linux/stat.h>
   7#include <linux/sched.h>
   8#include <linux/kernel.h>
   9#include <linux/mm.h>
  10#include <linux/smp.h>
  11#include <linux/smp_lock.h>
  12#include <linux/shm.h>
  13#include <linux/errno.h>
  14#include <linux/mman.h>
  15#include <linux/string.h>
  16#include <linux/slab.h>
  17
  18#include <asm/uaccess.h>
  19#include <asm/system.h>
  20#include <asm/pgtable.h>
  21
  22static inline void change_pte_range(pmd_t * pmd, unsigned long address,
  23        unsigned long size, pgprot_t newprot)
  24{
  25        pte_t * pte;
  26        unsigned long end;
  27
  28        if (pmd_none(*pmd))
  29                return;
  30        if (pmd_bad(*pmd)) {
  31                printk("change_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd));
  32                pmd_clear(pmd);
  33                return;
  34        }
  35        pte = pte_offset(pmd, address);
  36        address &= ~PMD_MASK;
  37        end = address + size;
  38        if (end > PMD_SIZE)
  39                end = PMD_SIZE;
  40        do {
  41                pte_t entry = *pte;
  42                if (pte_present(entry))
  43                        set_pte(pte, pte_modify(entry, newprot));
  44                address += PAGE_SIZE;
  45                pte++;
  46        } while (address < end);
  47}
  48
  49static inline void change_pmd_range(pgd_t * pgd, unsigned long address,
  50        unsigned long size, pgprot_t newprot)
  51{
  52        pmd_t * pmd;
  53        unsigned long end;
  54
  55        if (pgd_none(*pgd))
  56                return;
  57        if (pgd_bad(*pgd)) {
  58                printk("change_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd));
  59                pgd_clear(pgd);
  60                return;
  61        }
  62        pmd = pmd_offset(pgd, address);
  63        address &= ~PGDIR_MASK;
  64        end = address + size;
  65        if (end > PGDIR_SIZE)
  66                end = PGDIR_SIZE;
  67        do {
  68                change_pte_range(pmd, address, end - address, newprot);
  69                address = (address + PMD_SIZE) & PMD_MASK;
  70                pmd++;
  71        } while (address < end);
  72}
  73
  74static void change_protection(unsigned long start, unsigned long end, pgprot_t newprot)
  75{
  76        pgd_t *dir;
  77        unsigned long beg = start;
  78
  79        dir = pgd_offset(current->mm, start);
  80        flush_cache_range(current->mm, beg, end);
  81        while (start < end) {
  82                change_pmd_range(dir, start, end - start, newprot);
  83                start = (start + PGDIR_SIZE) & PGDIR_MASK;
  84                dir++;
  85        }
  86        flush_tlb_range(current->mm, beg, end);
  87        return;
  88}
  89
  90static inline int mprotect_fixup_all(struct vm_area_struct * vma,
  91        int newflags, pgprot_t prot)
  92{
  93        vma->vm_flags = newflags;
  94        vma->vm_page_prot = prot;
  95        return 0;
  96}
  97
  98static inline int mprotect_fixup_start(struct vm_area_struct * vma,
  99        unsigned long end,
 100        int newflags, pgprot_t prot)
 101{
 102        struct vm_area_struct * n;
 103
 104        n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
 105        if (!n)
 106                return -ENOMEM;
 107        *n = *vma;
 108        vma->vm_start = end;
 109        n->vm_end = end;
 110        vma->vm_offset += vma->vm_start - n->vm_start;
 111        n->vm_flags = newflags;
 112        n->vm_page_prot = prot;
 113        n->vm_dentry = dget(n->vm_dentry);
 114        if (n->vm_ops && n->vm_ops->open)
 115                n->vm_ops->open(n);
 116        insert_vm_struct(current->mm, n);
 117        return 0;
 118}
 119
 120static inline int mprotect_fixup_end(struct vm_area_struct * vma,
 121        unsigned long start,
 122        int newflags, pgprot_t prot)
 123{
 124        struct vm_area_struct * n;
 125
 126        n = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
 127        if (!n)
 128                return -ENOMEM;
 129        *n = *vma;
 130        vma->vm_end = start;
 131        n->vm_start = start;
 132        n->vm_offset += n->vm_start - vma->vm_start;
 133        n->vm_flags = newflags;
 134        n->vm_page_prot = prot;
 135        n->vm_dentry = dget(n->vm_dentry);
 136        if (n->vm_ops && n->vm_ops->open)
 137                n->vm_ops->open(n);
 138        insert_vm_struct(current->mm, n);
 139        return 0;
 140}
 141
 142static inline int mprotect_fixup_middle(struct vm_area_struct * vma,
 143        unsigned long start, unsigned long end,
 144        int newflags, pgprot_t prot)
 145{
 146        struct vm_area_struct * left, * right;
 147
 148        left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
 149        if (!left)
 150                return -ENOMEM;
 151        right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
 152        if (!right) {
 153                kmem_cache_free(vm_area_cachep, left);
 154                return -ENOMEM;
 155        }
 156        *left = *vma;
 157        *right = *vma;
 158        left->vm_end = start;
 159        vma->vm_start = start;
 160        vma->vm_end = end;
 161        right->vm_start = end;
 162        vma->vm_offset += vma->vm_start - left->vm_start;
 163        right->vm_offset += right->vm_start - left->vm_start;
 164        vma->vm_flags = newflags;
 165        vma->vm_page_prot = prot;
 166        if (vma->vm_dentry)
 167                vma->vm_dentry->d_count += 2;
 168        if (vma->vm_ops && vma->vm_ops->open) {
 169                vma->vm_ops->open(left);
 170                vma->vm_ops->open(right);
 171        }
 172        insert_vm_struct(current->mm, left);
 173        insert_vm_struct(current->mm, right);
 174        return 0;
 175}
 176
 177static int mprotect_fixup(struct vm_area_struct * vma, 
 178        unsigned long start, unsigned long end, unsigned int newflags)
 179{
 180        pgprot_t newprot;
 181        int error;
 182
 183        if (newflags == vma->vm_flags)
 184                return 0;
 185        newprot = protection_map[newflags & 0xf];
 186        if (start == vma->vm_start)
 187                if (end == vma->vm_end)
 188                        error = mprotect_fixup_all(vma, newflags, newprot);
 189                else
 190                        error = mprotect_fixup_start(vma, end, newflags, newprot);
 191        else if (end == vma->vm_end)
 192                error = mprotect_fixup_end(vma, start, newflags, newprot);
 193        else
 194                error = mprotect_fixup_middle(vma, start, end, newflags, newprot);
 195
 196        if (error)
 197                return error;
 198
 199        change_protection(start, end, newprot);
 200        return 0;
 201}
 202
 203asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot)
 204{
 205        unsigned long nstart, end, tmp;
 206        struct vm_area_struct * vma, * next;
 207        int error = -EINVAL;
 208
 209        lock_kernel();
 210        if (start & ~PAGE_MASK)
 211                goto out;
 212        len = (len + ~PAGE_MASK) & PAGE_MASK;
 213        end = start + len;
 214        if (end < start)
 215                goto out;
 216        if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
 217                goto out;
 218        error = 0;
 219        if (end == start)
 220                goto out;
 221        vma = find_vma(current->mm, start);
 222        error = -EFAULT;
 223        if (!vma || vma->vm_start > start)
 224                goto out;
 225
 226        for (nstart = start ; ; ) {
 227                unsigned int newflags;
 228
 229                /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
 230
 231                newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC));
 232                if ((newflags & ~(newflags >> 4)) & 0xf) {
 233                        error = -EACCES;
 234                        break;
 235                }
 236
 237                if (vma->vm_end >= end) {
 238                        error = mprotect_fixup(vma, nstart, end, newflags);
 239                        break;
 240                }
 241
 242                tmp = vma->vm_end;
 243                next = vma->vm_next;
 244                error = mprotect_fixup(vma, nstart, tmp, newflags);
 245                if (error)
 246                        break;
 247                nstart = tmp;
 248                vma = next;
 249                if (!vma || vma->vm_start != nstart) {
 250                        error = -EFAULT;
 251                        break;
 252                }
 253        }
 254        merge_segments(current->mm, start, end);
 255out:
 256        unlock_kernel();
 257        return error;
 258}
 259
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.