mmap.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <Kernel/FileSystem/FileDescription.h>
  27. #include <Kernel/Process.h>
  28. #include <Kernel/VM/PageDirectory.h>
  29. #include <Kernel/VM/PurgeableVMObject.h>
  30. #include <Kernel/VM/Region.h>
  31. #include <Kernel/VM/SharedInodeVMObject.h>
  32. #include <LibC/limits.h>
  33. namespace Kernel {
  34. static bool validate_mmap_prot(int prot, bool map_stack)
  35. {
  36. bool readable = prot & PROT_READ;
  37. bool writable = prot & PROT_WRITE;
  38. bool executable = prot & PROT_EXEC;
  39. if (writable && executable)
  40. return false;
  41. if (map_stack) {
  42. if (executable)
  43. return false;
  44. if (!readable || !writable)
  45. return false;
  46. }
  47. return true;
  48. }
  49. static bool validate_inode_mmap_prot(const Process& process, int prot, const Inode& inode, bool map_shared)
  50. {
  51. auto metadata = inode.metadata();
  52. if ((prot & PROT_READ) && !metadata.may_read(process))
  53. return false;
  54. if (map_shared) {
  55. // FIXME: What about readonly filesystem mounts? We cannot make a
  56. // decision here without knowing the mount flags, so we would need to
  57. // keep a Custody or something from mmap time.
  58. if ((prot & PROT_WRITE) && !metadata.may_write(process))
  59. return false;
  60. InterruptDisabler disabler;
  61. if (inode.shared_vmobject()) {
  62. if ((prot & PROT_EXEC) && inode.shared_vmobject()->writable_mappings())
  63. return false;
  64. if ((prot & PROT_WRITE) && inode.shared_vmobject()->executable_mappings())
  65. return false;
  66. }
  67. }
  68. return true;
  69. }
  70. void* Process::sys$mmap(Userspace<const Syscall::SC_mmap_params*> user_params)
  71. {
  72. REQUIRE_PROMISE(stdio);
  73. Syscall::SC_mmap_params params;
  74. if (!copy_from_user(&params, user_params))
  75. return (void*)-EFAULT;
  76. void* addr = (void*)params.addr;
  77. size_t size = params.size;
  78. size_t alignment = params.alignment;
  79. int prot = params.prot;
  80. int flags = params.flags;
  81. int fd = params.fd;
  82. int offset = params.offset;
  83. if (alignment & ~PAGE_MASK)
  84. return (void*)-EINVAL;
  85. if (!is_user_range(VirtualAddress(addr), size))
  86. return (void*)-EFAULT;
  87. String name;
  88. if (params.name.characters) {
  89. if (params.name.length > PATH_MAX)
  90. return (void*)-ENAMETOOLONG;
  91. name = copy_string_from_user(params.name);
  92. if (name.is_null())
  93. return (void*)-EFAULT;
  94. }
  95. if (size == 0)
  96. return (void*)-EINVAL;
  97. if ((FlatPtr)addr & ~PAGE_MASK)
  98. return (void*)-EINVAL;
  99. bool map_shared = flags & MAP_SHARED;
  100. bool map_anonymous = flags & MAP_ANONYMOUS;
  101. bool map_purgeable = flags & MAP_PURGEABLE;
  102. bool map_private = flags & MAP_PRIVATE;
  103. bool map_stack = flags & MAP_STACK;
  104. bool map_fixed = flags & MAP_FIXED;
  105. if (map_shared && map_private)
  106. return (void*)-EINVAL;
  107. if (!map_shared && !map_private)
  108. return (void*)-EINVAL;
  109. if (!validate_mmap_prot(prot, map_stack))
  110. return (void*)-EINVAL;
  111. if (map_stack && (!map_private || !map_anonymous))
  112. return (void*)-EINVAL;
  113. Region* region = nullptr;
  114. auto range = allocate_range(VirtualAddress(addr), size, alignment);
  115. if (!range.is_valid())
  116. return (void*)-ENOMEM;
  117. if (map_purgeable) {
  118. auto vmobject = PurgeableVMObject::create_with_size(size);
  119. region = allocate_region_with_vmobject(range, vmobject, 0, !name.is_null() ? name : "mmap (purgeable)", prot);
  120. if (!region && (!map_fixed && addr != 0))
  121. region = allocate_region_with_vmobject({}, size, vmobject, 0, !name.is_null() ? name : "mmap (purgeable)", prot);
  122. } else if (map_anonymous) {
  123. region = allocate_region(range, !name.is_null() ? name : "mmap", prot, false);
  124. if (!region && (!map_fixed && addr != 0))
  125. region = allocate_region(allocate_range({}, size), !name.is_null() ? name : "mmap", prot, false);
  126. } else {
  127. if (offset < 0)
  128. return (void*)-EINVAL;
  129. if (static_cast<size_t>(offset) & ~PAGE_MASK)
  130. return (void*)-EINVAL;
  131. auto description = file_description(fd);
  132. if (!description)
  133. return (void*)-EBADF;
  134. if (description->is_directory())
  135. return (void*)-ENODEV;
  136. // Require read access even when read protection is not requested.
  137. if (!description->is_readable())
  138. return (void*)-EACCES;
  139. if (map_shared) {
  140. if ((prot & PROT_WRITE) && !description->is_writable())
  141. return (void*)-EACCES;
  142. }
  143. if (description->inode()) {
  144. if (!validate_inode_mmap_prot(*this, prot, *description->inode(), map_shared))
  145. return (void*)-EACCES;
  146. }
  147. auto region_or_error = description->mmap(*this, VirtualAddress(addr), static_cast<size_t>(offset), size, prot, map_shared);
  148. if (region_or_error.is_error()) {
  149. // Fail if MAP_FIXED or address is 0, retry otherwise
  150. if (map_fixed || addr == 0)
  151. return (void*)(int)region_or_error.error();
  152. region_or_error = description->mmap(*this, {}, static_cast<size_t>(offset), size, prot, map_shared);
  153. }
  154. if (region_or_error.is_error())
  155. return (void*)(int)region_or_error.error();
  156. region = region_or_error.value();
  157. }
  158. if (!region)
  159. return (void*)-ENOMEM;
  160. region->set_mmap(true);
  161. if (map_shared)
  162. region->set_shared(true);
  163. if (map_stack)
  164. region->set_stack(true);
  165. if (!name.is_null())
  166. region->set_name(name);
  167. return region->vaddr().as_ptr();
  168. }
  169. int Process::sys$mprotect(void* addr, size_t size, int prot)
  170. {
  171. REQUIRE_PROMISE(stdio);
  172. if (!size)
  173. return -EINVAL;
  174. if (!is_user_range(VirtualAddress(addr), size))
  175. return -EFAULT;
  176. Range range_to_mprotect = { VirtualAddress(addr), size };
  177. if (auto* whole_region = find_region_from_range(range_to_mprotect)) {
  178. if (!whole_region->is_mmap())
  179. return -EPERM;
  180. if (!validate_mmap_prot(prot, whole_region->is_stack()))
  181. return -EINVAL;
  182. if (whole_region->access() == prot_to_region_access_flags(prot))
  183. return 0;
  184. if (whole_region->vmobject().is_inode()
  185. && !validate_inode_mmap_prot(*this, prot, static_cast<const InodeVMObject&>(whole_region->vmobject()).inode(), whole_region->is_shared())) {
  186. return -EACCES;
  187. }
  188. whole_region->set_readable(prot & PROT_READ);
  189. whole_region->set_writable(prot & PROT_WRITE);
  190. whole_region->set_executable(prot & PROT_EXEC);
  191. whole_region->remap();
  192. return 0;
  193. }
  194. // Check if we can carve out the desired range from an existing region
  195. if (auto* old_region = find_region_containing(range_to_mprotect)) {
  196. if (!old_region->is_mmap())
  197. return -EPERM;
  198. if (!validate_mmap_prot(prot, old_region->is_stack()))
  199. return -EINVAL;
  200. if (old_region->access() == prot_to_region_access_flags(prot))
  201. return 0;
  202. if (old_region->vmobject().is_inode()
  203. && !validate_inode_mmap_prot(*this, prot, static_cast<const InodeVMObject&>(old_region->vmobject()).inode(), old_region->is_shared())) {
  204. return -EACCES;
  205. }
  206. // This vector is the region(s) adjacent to our range.
  207. // We need to allocate a new region for the range we wanted to change permission bits on.
  208. auto adjacent_regions = split_region_around_range(*old_region, range_to_mprotect);
  209. size_t new_range_offset_in_vmobject = old_region->offset_in_vmobject() + (range_to_mprotect.base().get() - old_region->range().base().get());
  210. auto& new_region = allocate_split_region(*old_region, range_to_mprotect, new_range_offset_in_vmobject);
  211. new_region.set_readable(prot & PROT_READ);
  212. new_region.set_writable(prot & PROT_WRITE);
  213. new_region.set_executable(prot & PROT_EXEC);
  214. // Unmap the old region here, specifying that we *don't* want the VM deallocated.
  215. old_region->unmap(Region::ShouldDeallocateVirtualMemoryRange::No);
  216. deallocate_region(*old_region);
  217. // Map the new regions using our page directory (they were just allocated and don't have one).
  218. for (auto* adjacent_region : adjacent_regions) {
  219. adjacent_region->map(page_directory());
  220. }
  221. new_region.map(page_directory());
  222. return 0;
  223. }
  224. // FIXME: We should also support mprotect() across multiple regions. (#175) (#964)
  225. return -EINVAL;
  226. }
  227. int Process::sys$madvise(void* address, size_t size, int advice)
  228. {
  229. REQUIRE_PROMISE(stdio);
  230. if (!size)
  231. return -EINVAL;
  232. if (!is_user_range(VirtualAddress(address), size))
  233. return -EFAULT;
  234. auto* region = find_region_from_range({ VirtualAddress(address), size });
  235. if (!region)
  236. return -EINVAL;
  237. if (!region->is_mmap())
  238. return -EPERM;
  239. if ((advice & MADV_SET_VOLATILE) && (advice & MADV_SET_NONVOLATILE))
  240. return -EINVAL;
  241. if (advice & MADV_SET_VOLATILE) {
  242. if (!region->vmobject().is_purgeable())
  243. return -EPERM;
  244. auto& vmobject = static_cast<PurgeableVMObject&>(region->vmobject());
  245. vmobject.set_volatile(true);
  246. return 0;
  247. }
  248. if (advice & MADV_SET_NONVOLATILE) {
  249. if (!region->vmobject().is_purgeable())
  250. return -EPERM;
  251. auto& vmobject = static_cast<PurgeableVMObject&>(region->vmobject());
  252. if (!vmobject.is_volatile())
  253. return 0;
  254. vmobject.set_volatile(false);
  255. bool was_purged = vmobject.was_purged();
  256. vmobject.set_was_purged(false);
  257. return was_purged ? 1 : 0;
  258. }
  259. if (advice & MADV_GET_VOLATILE) {
  260. if (!region->vmobject().is_purgeable())
  261. return -EPERM;
  262. auto& vmobject = static_cast<PurgeableVMObject&>(region->vmobject());
  263. return vmobject.is_volatile() ? 0 : 1;
  264. }
  265. return -EINVAL;
  266. }
  267. int Process::sys$minherit(void* address, size_t size, int inherit)
  268. {
  269. REQUIRE_PROMISE(stdio);
  270. auto* region = find_region_from_range({ VirtualAddress(address), size });
  271. if (!region)
  272. return -EINVAL;
  273. if (!region->is_mmap())
  274. return -EINVAL;
  275. if (region->is_shared())
  276. return -EINVAL;
  277. if (!region->vmobject().is_anonymous())
  278. return -EINVAL;
  279. switch (inherit) {
  280. case MAP_INHERIT_ZERO:
  281. region->set_inherit_mode(Region::InheritMode::ZeroedOnFork);
  282. return 0;
  283. }
  284. return -EINVAL;
  285. }
  286. int Process::sys$set_mmap_name(Userspace<const Syscall::SC_set_mmap_name_params*> user_params)
  287. {
  288. REQUIRE_PROMISE(stdio);
  289. Syscall::SC_set_mmap_name_params params;
  290. if (!copy_from_user(&params, user_params))
  291. return -EFAULT;
  292. if (params.name.length > PATH_MAX)
  293. return -ENAMETOOLONG;
  294. auto name = copy_string_from_user(params.name);
  295. if (name.is_null())
  296. return -EFAULT;
  297. auto* region = find_region_from_range({ VirtualAddress(params.addr), params.size });
  298. if (!region)
  299. return -EINVAL;
  300. if (!region->is_mmap())
  301. return -EPERM;
  302. region->set_name(move(name));
  303. return 0;
  304. }
  305. // Carve out a virtual address range from a region and return the two regions on either side
  306. Vector<Region*, 2> Process::split_region_around_range(const Region& source_region, const Range& desired_range)
  307. {
  308. Range old_region_range = source_region.range();
  309. auto remaining_ranges_after_unmap = old_region_range.carve(desired_range);
  310. ASSERT(!remaining_ranges_after_unmap.is_empty());
  311. auto make_replacement_region = [&](const Range& new_range) -> Region& {
  312. ASSERT(old_region_range.contains(new_range));
  313. size_t new_range_offset_in_vmobject = source_region.offset_in_vmobject() + (new_range.base().get() - old_region_range.base().get());
  314. return allocate_split_region(source_region, new_range, new_range_offset_in_vmobject);
  315. };
  316. Vector<Region*, 2> new_regions;
  317. for (auto& new_range : remaining_ranges_after_unmap) {
  318. new_regions.unchecked_append(&make_replacement_region(new_range));
  319. }
  320. return new_regions;
  321. }
  322. int Process::sys$munmap(void* addr, size_t size)
  323. {
  324. REQUIRE_PROMISE(stdio);
  325. if (!size)
  326. return -EINVAL;
  327. if (!is_user_range(VirtualAddress(addr), size))
  328. return -EFAULT;
  329. Range range_to_unmap { VirtualAddress(addr), size };
  330. if (auto* whole_region = find_region_from_range(range_to_unmap)) {
  331. if (!whole_region->is_mmap())
  332. return -EPERM;
  333. bool success = deallocate_region(*whole_region);
  334. ASSERT(success);
  335. return 0;
  336. }
  337. if (auto* old_region = find_region_containing(range_to_unmap)) {
  338. if (!old_region->is_mmap())
  339. return -EPERM;
  340. auto new_regions = split_region_around_range(*old_region, range_to_unmap);
  341. // We manually unmap the old region here, specifying that we *don't* want the VM deallocated.
  342. old_region->unmap(Region::ShouldDeallocateVirtualMemoryRange::No);
  343. deallocate_region(*old_region);
  344. // Instead we give back the unwanted VM manually.
  345. page_directory().range_allocator().deallocate(range_to_unmap);
  346. // And finally we map the new region(s) using our page directory (they were just allocated and don't have one).
  347. for (auto* new_region : new_regions) {
  348. new_region->map(page_directory());
  349. }
  350. return 0;
  351. }
  352. // FIXME: We should also support munmap() across multiple regions. (#175)
  353. return -EINVAL;
  354. }
  355. }