mprotect.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * linux/mm/mprotect.c
  3.  *
  4.  *  (C) Copyright 1994 Linus Torvalds
  5.  */
  6. #include <linux/slab.h>
  7. #include <linux/smp_lock.h>
  8. #include <linux/shm.h>
  9. #include <linux/mman.h>
  10. #include <asm/uaccess.h>
  11. #include <asm/pgalloc.h>
  12. #include <asm/pgtable.h>
  13. static inline void change_pte_range(pmd_t * pmd, unsigned long address,
  14. unsigned long size, pgprot_t newprot)
  15. {
  16. pte_t * pte;
  17. unsigned long end;
  18. if (pmd_none(*pmd))
  19. return;
  20. if (pmd_bad(*pmd)) {
  21. pmd_ERROR(*pmd);
  22. pmd_clear(pmd);
  23. return;
  24. }
  25. pte = pte_offset(pmd, address);
  26. address &= ~PMD_MASK;
  27. end = address + size;
  28. if (end > PMD_SIZE)
  29. end = PMD_SIZE;
  30. do {
  31. if (pte_present(*pte)) {
  32. pte_t entry;
  33. /* Avoid an SMP race with hardware updated dirty/clean
  34.  * bits by wiping the pte and then setting the new pte
  35.  * into place.
  36.  */
  37. entry = ptep_get_and_clear(pte);
  38. set_pte(pte, pte_modify(entry, newprot));
  39. }
  40. address += PAGE_SIZE;
  41. pte++;
  42. } while (address && (address < end));
  43. }
  44. static inline void change_pmd_range(pgd_t * pgd, unsigned long address,
  45. unsigned long size, pgprot_t newprot)
  46. {
  47. pmd_t * pmd;
  48. unsigned long end;
  49. if (pgd_none(*pgd))
  50. return;
  51. if (pgd_bad(*pgd)) {
  52. pgd_ERROR(*pgd);
  53. pgd_clear(pgd);
  54. return;
  55. }
  56. pmd = pmd_offset(pgd, address);
  57. address &= ~PGDIR_MASK;
  58. end = address + size;
  59. if (end > PGDIR_SIZE)
  60. end = PGDIR_SIZE;
  61. do {
  62. change_pte_range(pmd, address, end - address, newprot);
  63. address = (address + PMD_SIZE) & PMD_MASK;
  64. pmd++;
  65. } while (address && (address < end));
  66. }
  67. static void change_protection(unsigned long start, unsigned long end, pgprot_t newprot)
  68. {
  69. pgd_t *dir;
  70. unsigned long beg = start;
  71. dir = pgd_offset(current->mm, start);
  72. flush_cache_range(current->mm, beg, end);
  73. if (start >= end)
  74. BUG();
  75. spin_lock(&current->mm->page_table_lock);
  76. do {
  77. change_pmd_range(dir, start, end - start, newprot);
  78. start = (start + PGDIR_SIZE) & PGDIR_MASK;
  79. dir++;
  80. } while (start && (start < end));
  81. spin_unlock(&current->mm->page_table_lock);
  82. flush_tlb_range(current->mm, beg, end);
  83. return;
  84. }
  85. static inline int mprotect_fixup_all(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
  86. int newflags, pgprot_t prot)
  87. {
  88. struct vm_area_struct * prev = *pprev;
  89. struct mm_struct * mm = vma->vm_mm;
  90. if (prev && prev->vm_end == vma->vm_start && can_vma_merge(prev, newflags) &&
  91.     !vma->vm_file && !(vma->vm_flags & VM_SHARED)) {
  92. spin_lock(&mm->page_table_lock);
  93. prev->vm_end = vma->vm_end;
  94. __vma_unlink(mm, vma, prev);
  95. spin_unlock(&mm->page_table_lock);
  96. kmem_cache_free(vm_area_cachep, vma);
  97. mm->map_count--;
  98. return 0;
  99. }
  100. spin_lock(&mm->page_table_lock);
  101. vma->vm_flags = newflags;
  102. vma->vm_page_prot = prot;
  103. spin_unlock(&mm->page_table_lock);
  104. *pprev = vma;
  105. return 0;
  106. }
  107. static inline int mprotect_fixup_start(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
  108. unsigned long end,
  109. int newflags, pgprot_t prot)
  110. {
  111. struct vm_area_struct * n, * prev = *pprev;
  112. *pprev = vma;
  113. if (prev && prev->vm_end == vma->vm_start && can_vma_merge(prev, newflags) &&
  114.     !vma->vm_file && !(vma->vm_flags & VM_SHARED)) {
  115. spin_lock(&vma->vm_mm->page_table_lock);
  116. prev->vm_end = end;
  117. vma->vm_start = end;
  118. spin_unlock(&vma->vm_mm->page_table_lock);
  119. return 0;
  120. }
  121. n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  122. if (!n)
  123. return -ENOMEM;
  124. *n = *vma;
  125. n->vm_end = end;
  126. n->vm_flags = newflags;
  127. n->vm_raend = 0;
  128. n->vm_page_prot = prot;
  129. if (n->vm_file)
  130. get_file(n->vm_file);
  131. if (n->vm_ops && n->vm_ops->open)
  132. n->vm_ops->open(n);
  133. vma->vm_pgoff += (end - vma->vm_start) >> PAGE_SHIFT;
  134. lock_vma_mappings(vma);
  135. spin_lock(&vma->vm_mm->page_table_lock);
  136. vma->vm_start = end;
  137. __insert_vm_struct(current->mm, n);
  138. spin_unlock(&vma->vm_mm->page_table_lock);
  139. unlock_vma_mappings(vma);
  140. return 0;
  141. }
  142. static inline int mprotect_fixup_end(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
  143. unsigned long start,
  144. int newflags, pgprot_t prot)
  145. {
  146. struct vm_area_struct * n;
  147. n = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
  148. if (!n)
  149. return -ENOMEM;
  150. *n = *vma;
  151. n->vm_start = start;
  152. n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
  153. n->vm_flags = newflags;
  154. n->vm_raend = 0;
  155. n->vm_page_prot = prot;
  156. if (n->vm_file)
  157. get_file(n->vm_file);
  158. if (n->vm_ops && n->vm_ops->open)
  159. n->vm_ops->open(n);
  160. lock_vma_mappings(vma);
  161. spin_lock(&vma->vm_mm->page_table_lock);
  162. vma->vm_end = start;
  163. __insert_vm_struct(current->mm, n);
  164. spin_unlock(&vma->vm_mm->page_table_lock);
  165. unlock_vma_mappings(vma);
  166. *pprev = n;
  167. return 0;
  168. }
  169. static inline int mprotect_fixup_middle(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
  170. unsigned long start, unsigned long end,
  171. int newflags, pgprot_t prot)
  172. {
  173. struct vm_area_struct * left, * right;
  174. left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  175. if (!left)
  176. return -ENOMEM;
  177. right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  178. if (!right) {
  179. kmem_cache_free(vm_area_cachep, left);
  180. return -ENOMEM;
  181. }
  182. *left = *vma;
  183. *right = *vma;
  184. left->vm_end = start;
  185. right->vm_start = end;
  186. right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
  187. left->vm_raend = 0;
  188. right->vm_raend = 0;
  189. if (vma->vm_file)
  190. atomic_add(2,&vma->vm_file->f_count);
  191. if (vma->vm_ops && vma->vm_ops->open) {
  192. vma->vm_ops->open(left);
  193. vma->vm_ops->open(right);
  194. }
  195. vma->vm_pgoff += (start - vma->vm_start) >> PAGE_SHIFT;
  196. vma->vm_raend = 0;
  197. vma->vm_page_prot = prot;
  198. lock_vma_mappings(vma);
  199. spin_lock(&vma->vm_mm->page_table_lock);
  200. vma->vm_start = start;
  201. vma->vm_end = end;
  202. vma->vm_flags = newflags;
  203. __insert_vm_struct(current->mm, left);
  204. __insert_vm_struct(current->mm, right);
  205. spin_unlock(&vma->vm_mm->page_table_lock);
  206. unlock_vma_mappings(vma);
  207. *pprev = right;
  208. return 0;
  209. }
  210. static int mprotect_fixup(struct vm_area_struct * vma, struct vm_area_struct ** pprev,
  211. unsigned long start, unsigned long end, unsigned int newflags)
  212. {
  213. pgprot_t newprot;
  214. int error;
  215. if (newflags == vma->vm_flags) {
  216. *pprev = vma;
  217. return 0;
  218. }
  219. newprot = protection_map[newflags & 0xf];
  220. if (start == vma->vm_start) {
  221. if (end == vma->vm_end)
  222. error = mprotect_fixup_all(vma, pprev, newflags, newprot);
  223. else
  224. error = mprotect_fixup_start(vma, pprev, end, newflags, newprot);
  225. } else if (end == vma->vm_end)
  226. error = mprotect_fixup_end(vma, pprev, start, newflags, newprot);
  227. else
  228. error = mprotect_fixup_middle(vma, pprev, start, end, newflags, newprot);
  229. if (error)
  230. return error;
  231. change_protection(start, end, newprot);
  232. return 0;
  233. }
  234. asmlinkage long sys_mprotect(unsigned long start, size_t len, unsigned long prot)
  235. {
  236. unsigned long nstart, end, tmp;
  237. struct vm_area_struct * vma, * next, * prev;
  238. int error = -EINVAL;
  239. if (start & ~PAGE_MASK)
  240. return -EINVAL;
  241. len = PAGE_ALIGN(len);
  242. end = start + len;
  243. if (end < start)
  244. return -EINVAL;
  245. if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
  246. return -EINVAL;
  247. if (end == start)
  248. return 0;
  249. down_write(&current->mm->mmap_sem);
  250. vma = find_vma_prev(current->mm, start, &prev);
  251. error = -ENOMEM;
  252. if (!vma || vma->vm_start > start)
  253. goto out;
  254. for (nstart = start ; ; ) {
  255. unsigned int newflags;
  256. int last = 0;
  257. /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
  258. newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC));
  259. if ((newflags & ~(newflags >> 4)) & 0xf) {
  260. error = -EACCES;
  261. goto out;
  262. }
  263. if (vma->vm_end > end) {
  264. error = mprotect_fixup(vma, &prev, nstart, end, newflags);
  265. goto out;
  266. }
  267. if (vma->vm_end == end)
  268. last = 1;
  269. tmp = vma->vm_end;
  270. next = vma->vm_next;
  271. error = mprotect_fixup(vma, &prev, nstart, tmp, newflags);
  272. if (error)
  273. goto out;
  274. if (last)
  275. break;
  276. nstart = tmp;
  277. vma = next;
  278. if (!vma || vma->vm_start != nstart) {
  279. error = -ENOMEM;
  280. goto out;
  281. }
  282. }
  283. if (next && prev->vm_end == next->vm_start && can_vma_merge(next, prev->vm_flags) &&
  284.     !prev->vm_file && !(prev->vm_flags & VM_SHARED)) {
  285. spin_lock(&prev->vm_mm->page_table_lock);
  286. prev->vm_end = next->vm_end;
  287. __vma_unlink(prev->vm_mm, next, prev);
  288. spin_unlock(&prev->vm_mm->page_table_lock);
  289. kmem_cache_free(vm_area_cachep, next);
  290. prev->vm_mm->map_count--;
  291. }
  292. out:
  293. up_write(&current->mm->mmap_sem);
  294. return error;
  295. }