mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:52:44 +00:00 
			
		
		
		
	Kernel: Implement basic support for sys$mmap() with MAP_PRIVATE
You can now mmap a file as private and writable, and the changes you make will only be visible to you. This works because internally a MAP_PRIVATE region is backed by a unique PrivateInodeVMObject instead of using the globally shared SharedInodeVMObject like we always did before. :^) Fixes #1045.
This commit is contained in:
		
							parent
							
								
									aa1e209845
								
							
						
					
					
						commit
						8fbdda5a2d
					
				
					 11 changed files with 45 additions and 31 deletions
				
			
		|  | @ -166,9 +166,11 @@ u32 BXVGADevice::find_framebuffer_address() | ||||||
|     return framebuffer_address; |     return framebuffer_address; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| KResultOr<Region*> BXVGADevice::mmap(Process& process, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot) | KResultOr<Region*> BXVGADevice::mmap(Process& process, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared) | ||||||
| { | { | ||||||
|     REQUIRE_PROMISE(video); |     REQUIRE_PROMISE(video); | ||||||
|  |     if (!shared) | ||||||
|  |         return KResult(-ENODEV); | ||||||
|     ASSERT(offset == 0); |     ASSERT(offset == 0); | ||||||
|     ASSERT(size == framebuffer_size_in_bytes()); |     ASSERT(size == framebuffer_size_in_bytes()); | ||||||
|     auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes()); |     auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes()); | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ public: | ||||||
|     BXVGADevice(); |     BXVGADevice(); | ||||||
| 
 | 
 | ||||||
|     virtual int ioctl(FileDescription&, unsigned request, unsigned arg) override; |     virtual int ioctl(FileDescription&, unsigned request, unsigned arg) override; | ||||||
|     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t, int prot) override; |     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t, int prot, bool shared) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     virtual const char* class_name() const override { return "BXVGA"; } |     virtual const char* class_name() const override { return "BXVGA"; } | ||||||
|  |  | ||||||
|  | @ -51,9 +51,11 @@ MBVGADevice::MBVGADevice(PhysicalAddress addr, int pitch, int width, int height) | ||||||
|     s_the = this; |     s_the = this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| KResultOr<Region*> MBVGADevice::mmap(Process& process, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot) | KResultOr<Region*> MBVGADevice::mmap(Process& process, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared) | ||||||
| { | { | ||||||
|     REQUIRE_PROMISE(video); |     REQUIRE_PROMISE(video); | ||||||
|  |     if (!shared) | ||||||
|  |         return KResult(-ENODEV); | ||||||
|     ASSERT(offset == 0); |     ASSERT(offset == 0); | ||||||
|     ASSERT(size == framebuffer_size_in_bytes()); |     ASSERT(size == framebuffer_size_in_bytes()); | ||||||
|     auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes()); |     auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes()); | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ public: | ||||||
|     MBVGADevice(PhysicalAddress addr, int pitch, int width, int height); |     MBVGADevice(PhysicalAddress addr, int pitch, int width, int height); | ||||||
| 
 | 
 | ||||||
|     virtual int ioctl(FileDescription&, unsigned request, unsigned arg) override; |     virtual int ioctl(FileDescription&, unsigned request, unsigned arg) override; | ||||||
|     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t, int prot) override; |     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t, int prot, bool shared) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     virtual const char* class_name() const override { return "MBVGA"; } |     virtual const char* class_name() const override { return "MBVGA"; } | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ int File::ioctl(FileDescription&, unsigned, unsigned) | ||||||
|     return -ENOTTY; |     return -ENOTTY; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| KResultOr<Region*> File::mmap(Process&, FileDescription&, VirtualAddress, size_t, size_t, int) | KResultOr<Region*> File::mmap(Process&, FileDescription&, VirtualAddress, size_t, size_t, int, bool) | ||||||
| { | { | ||||||
|     return KResult(-ENODEV); |     return KResult(-ENODEV); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -77,7 +77,7 @@ public: | ||||||
|     virtual ssize_t read(FileDescription&, u8*, ssize_t) = 0; |     virtual ssize_t read(FileDescription&, u8*, ssize_t) = 0; | ||||||
|     virtual ssize_t write(FileDescription&, const u8*, ssize_t) = 0; |     virtual ssize_t write(FileDescription&, const u8*, ssize_t) = 0; | ||||||
|     virtual int ioctl(FileDescription&, unsigned request, unsigned arg); |     virtual int ioctl(FileDescription&, unsigned request, unsigned arg); | ||||||
|     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot); |     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared); | ||||||
| 
 | 
 | ||||||
|     virtual String absolute_path(const FileDescription&) const = 0; |     virtual String absolute_path(const FileDescription&) const = 0; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -281,10 +281,10 @@ InodeMetadata FileDescription::metadata() const | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| KResultOr<Region*> FileDescription::mmap(Process& process, VirtualAddress vaddr, size_t offset, size_t size, int prot) | KResultOr<Region*> FileDescription::mmap(Process& process, VirtualAddress vaddr, size_t offset, size_t size, int prot, bool shared) | ||||||
| { | { | ||||||
|     LOCKER(m_lock); |     LOCKER(m_lock); | ||||||
|     return m_file->mmap(process, *this, vaddr, offset, size, prot); |     return m_file->mmap(process, *this, vaddr, offset, size, prot, shared); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| KResult FileDescription::truncate(u64 length) | KResult FileDescription::truncate(u64 length) | ||||||
|  |  | ||||||
|  | @ -109,7 +109,7 @@ public: | ||||||
|     Custody* custody() { return m_custody.ptr(); } |     Custody* custody() { return m_custody.ptr(); } | ||||||
|     const Custody* custody() const { return m_custody.ptr(); } |     const Custody* custody() const { return m_custody.ptr(); } | ||||||
| 
 | 
 | ||||||
|     KResultOr<Region*> mmap(Process&, VirtualAddress, size_t offset, size_t, int prot); |     KResultOr<Region*> mmap(Process&, VirtualAddress, size_t offset, size_t, int prot, bool shared); | ||||||
| 
 | 
 | ||||||
|     bool is_blocking() const { return m_is_blocking; } |     bool is_blocking() const { return m_is_blocking; } | ||||||
|     void set_blocking(bool b) { m_is_blocking = b; } |     void set_blocking(bool b) { m_is_blocking = b; } | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
| #include <Kernel/FileSystem/InodeFile.h> | #include <Kernel/FileSystem/InodeFile.h> | ||||||
| #include <Kernel/FileSystem/VirtualFileSystem.h> | #include <Kernel/FileSystem/VirtualFileSystem.h> | ||||||
| #include <Kernel/Process.h> | #include <Kernel/Process.h> | ||||||
|  | #include <Kernel/VM/PrivateInodeVMObject.h> | ||||||
| #include <Kernel/VM/SharedInodeVMObject.h> | #include <Kernel/VM/SharedInodeVMObject.h> | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
|  | @ -60,11 +61,18 @@ ssize_t InodeFile::write(FileDescription& description, const u8* data, ssize_t c | ||||||
|     return nwritten; |     return nwritten; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| KResultOr<Region*> InodeFile::mmap(Process& process, FileDescription& description, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot) | KResultOr<Region*> InodeFile::mmap(Process& process, FileDescription& description, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared) | ||||||
| { | { | ||||||
|     ASSERT(offset == 0); |     ASSERT(offset == 0); | ||||||
|     // FIXME: If PROT_EXEC, check that the underlying file system isn't mounted noexec.
 |     // FIXME: If PROT_EXEC, check that the underlying file system isn't mounted noexec.
 | ||||||
|     auto* region = process.allocate_region_with_vmobject(preferred_vaddr, size, SharedInodeVMObject::create_with_inode(inode()), offset, description.absolute_path(), prot); |     RefPtr<InodeVMObject> vmobject; | ||||||
|  |     if (shared) | ||||||
|  |         vmobject = SharedInodeVMObject::create_with_inode(inode()); | ||||||
|  |     else | ||||||
|  |         vmobject = PrivateInodeVMObject::create_with_inode(inode()); | ||||||
|  |     if (!vmobject) | ||||||
|  |         return KResult(-ENOMEM); | ||||||
|  |     auto* region = process.allocate_region_with_vmobject(preferred_vaddr, size, *vmobject, offset, description.absolute_path(), prot); | ||||||
|     if (!region) |     if (!region) | ||||||
|         return KResult(-ENOMEM); |         return KResult(-ENOMEM); | ||||||
|     return region; |     return region; | ||||||
|  |  | ||||||
|  | @ -49,7 +49,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual ssize_t read(FileDescription&, u8*, ssize_t) override; |     virtual ssize_t read(FileDescription&, u8*, ssize_t) override; | ||||||
|     virtual ssize_t write(FileDescription&, const u8*, ssize_t) override; |     virtual ssize_t write(FileDescription&, const u8*, ssize_t) override; | ||||||
|     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot) override; |     virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot, bool shared) override; | ||||||
| 
 | 
 | ||||||
|     virtual String absolute_path(const FileDescription&) const override; |     virtual String absolute_path(const FileDescription&) const override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -321,13 +321,15 @@ static bool validate_mmap_prot(int prot, bool map_stack) | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool validate_inode_mmap_prot(const Process& process, int prot, const Inode& inode) | static bool validate_inode_mmap_prot(const Process& process, int prot, const Inode& inode, bool map_shared) | ||||||
| { | { | ||||||
|     auto metadata = inode.metadata(); |     auto metadata = inode.metadata(); | ||||||
|     if ((prot & PROT_WRITE) && !metadata.may_write(process)) |  | ||||||
|         return false; |  | ||||||
|     if ((prot & PROT_READ) && !metadata.may_read(process)) |     if ((prot & PROT_READ) && !metadata.may_read(process)) | ||||||
|         return false; |         return false; | ||||||
|  | 
 | ||||||
|  |     if (map_shared) { | ||||||
|  |         if ((prot & PROT_WRITE) && !metadata.may_write(process)) | ||||||
|  |             return false; | ||||||
|         InterruptDisabler disabler; |         InterruptDisabler disabler; | ||||||
|         if (inode.shared_vmobject()) { |         if (inode.shared_vmobject()) { | ||||||
|             if ((prot & PROT_EXEC) && inode.shared_vmobject()->writable_mappings()) |             if ((prot & PROT_EXEC) && inode.shared_vmobject()->writable_mappings()) | ||||||
|  | @ -335,6 +337,7 @@ static bool validate_inode_mmap_prot(const Process& process, int prot, const Ino | ||||||
|             if ((prot & PROT_WRITE) && inode.shared_vmobject()->executable_mappings()) |             if ((prot & PROT_WRITE) && inode.shared_vmobject()->executable_mappings()) | ||||||
|                 return false; |                 return false; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -433,9 +436,6 @@ void* Process::sys$mmap(const Syscall::SC_mmap_params* user_params) | ||||||
|             return (void*)-EINVAL; |             return (void*)-EINVAL; | ||||||
|         if (static_cast<size_t>(offset) & ~PAGE_MASK) |         if (static_cast<size_t>(offset) & ~PAGE_MASK) | ||||||
|             return (void*)-EINVAL; |             return (void*)-EINVAL; | ||||||
|         // FIXME: Implement MAP_PRIVATE for FileDescription-backed mmap
 |  | ||||||
|         if (map_private) |  | ||||||
|             return (void*)-ENOTSUP; |  | ||||||
|         auto description = file_description(fd); |         auto description = file_description(fd); | ||||||
|         if (!description) |         if (!description) | ||||||
|             return (void*)-EBADF; |             return (void*)-EBADF; | ||||||
|  | @ -443,18 +443,20 @@ void* Process::sys$mmap(const Syscall::SC_mmap_params* user_params) | ||||||
|             return (void*)-ENODEV; |             return (void*)-ENODEV; | ||||||
|         if ((prot & PROT_READ) && !description->is_readable()) |         if ((prot & PROT_READ) && !description->is_readable()) | ||||||
|             return (void*)-EACCES; |             return (void*)-EACCES; | ||||||
|  |         if (map_shared) { | ||||||
|             if ((prot & PROT_WRITE) && !description->is_writable()) |             if ((prot & PROT_WRITE) && !description->is_writable()) | ||||||
|                 return (void*)-EACCES; |                 return (void*)-EACCES; | ||||||
|  |         } | ||||||
|         if (description->inode()) { |         if (description->inode()) { | ||||||
|             if (!validate_inode_mmap_prot(*this, prot, *description->inode())) |             if (!validate_inode_mmap_prot(*this, prot, *description->inode(), map_shared)) | ||||||
|                 return (void*)-EACCES; |                 return (void*)-EACCES; | ||||||
|         } |         } | ||||||
|         auto region_or_error = description->mmap(*this, VirtualAddress(addr), static_cast<size_t>(offset), size, prot); |         auto region_or_error = description->mmap(*this, VirtualAddress(addr), static_cast<size_t>(offset), size, prot, map_shared); | ||||||
|         if (region_or_error.is_error()) { |         if (region_or_error.is_error()) { | ||||||
|             // Fail if MAP_FIXED or address is 0, retry otherwise
 |             // Fail if MAP_FIXED or address is 0, retry otherwise
 | ||||||
|             if (map_fixed || addr == 0) |             if (map_fixed || addr == 0) | ||||||
|                 return (void*)(int)region_or_error.error(); |                 return (void*)(int)region_or_error.error(); | ||||||
|             region_or_error = description->mmap(*this, {}, static_cast<size_t>(offset), size, prot); |             region_or_error = description->mmap(*this, {}, static_cast<size_t>(offset), size, prot, map_shared); | ||||||
|         } |         } | ||||||
|         if (region_or_error.is_error()) |         if (region_or_error.is_error()) | ||||||
|             return (void*)(int)region_or_error.error(); |             return (void*)(int)region_or_error.error(); | ||||||
|  | @ -537,7 +539,7 @@ int Process::sys$mprotect(void* addr, size_t size, int prot) | ||||||
|         if (whole_region->access() == prot_to_region_access_flags(prot)) |         if (whole_region->access() == prot_to_region_access_flags(prot)) | ||||||
|             return 0; |             return 0; | ||||||
|         if (whole_region->vmobject().is_inode() |         if (whole_region->vmobject().is_inode() | ||||||
|             && !validate_inode_mmap_prot(*this, prot, static_cast<const SharedInodeVMObject&>(whole_region->vmobject()).inode())) { |             && !validate_inode_mmap_prot(*this, prot, static_cast<const SharedInodeVMObject&>(whole_region->vmobject()).inode(), whole_region->is_shared())) { | ||||||
|             return -EACCES; |             return -EACCES; | ||||||
|         } |         } | ||||||
|         whole_region->set_readable(prot & PROT_READ); |         whole_region->set_readable(prot & PROT_READ); | ||||||
|  | @ -556,7 +558,7 @@ int Process::sys$mprotect(void* addr, size_t size, int prot) | ||||||
|         if (old_region->access() == prot_to_region_access_flags(prot)) |         if (old_region->access() == prot_to_region_access_flags(prot)) | ||||||
|             return 0; |             return 0; | ||||||
|         if (old_region->vmobject().is_inode() |         if (old_region->vmobject().is_inode() | ||||||
|             && !validate_inode_mmap_prot(*this, prot, static_cast<const SharedInodeVMObject&>(old_region->vmobject()).inode())) { |             && !validate_inode_mmap_prot(*this, prot, static_cast<const SharedInodeVMObject&>(old_region->vmobject()).inode(), old_region->is_shared())) { | ||||||
|             return -EACCES; |             return -EACCES; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling