mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 08:08:12 +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,19 +321,22 @@ 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;
|
||||||
InterruptDisabler disabler;
|
|
||||||
if (inode.shared_vmobject()) {
|
if (map_shared) {
|
||||||
if ((prot & PROT_EXEC) && inode.shared_vmobject()->writable_mappings())
|
if ((prot & PROT_WRITE) && !metadata.may_write(process))
|
||||||
return false;
|
|
||||||
if ((prot & PROT_WRITE) && inode.shared_vmobject()->executable_mappings())
|
|
||||||
return false;
|
return false;
|
||||||
|
InterruptDisabler disabler;
|
||||||
|
if (inode.shared_vmobject()) {
|
||||||
|
if ((prot & PROT_EXEC) && inode.shared_vmobject()->writable_mappings())
|
||||||
|
return false;
|
||||||
|
if ((prot & PROT_WRITE) && inode.shared_vmobject()->executable_mappings())
|
||||||
|
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 ((prot & PROT_WRITE) && !description->is_writable())
|
if (map_shared) {
|
||||||
return (void*)-EACCES;
|
if ((prot & PROT_WRITE) && !description->is_writable())
|
||||||
if (description->inode()) {
|
|
||||||
if (!validate_inode_mmap_prot(*this, prot, *description->inode()))
|
|
||||||
return (void*)-EACCES;
|
return (void*)-EACCES;
|
||||||
}
|
}
|
||||||
auto region_or_error = description->mmap(*this, VirtualAddress(addr), static_cast<size_t>(offset), size, prot);
|
if (description->inode()) {
|
||||||
|
if (!validate_inode_mmap_prot(*this, prot, *description->inode(), map_shared))
|
||||||
|
return (void*)-EACCES;
|
||||||
|
}
|
||||||
|
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