From 3ad0e1a1d595a931e21c8f4ccfb15f61379fa647 Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 6 Aug 2022 21:05:48 +0300 Subject: [PATCH] Kernel: Handle mmap requests on zero-length data file inodes safely --- Kernel/FileSystem/InodeFile.cpp | 6 +++--- Kernel/Memory/PrivateInodeVMObject.cpp | 13 ++++++++++++- Kernel/Memory/PrivateInodeVMObject.h | 1 + Kernel/Memory/SharedInodeVMObject.cpp | 12 +++++++++++- Kernel/Memory/SharedInodeVMObject.h | 1 + 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Kernel/FileSystem/InodeFile.cpp b/Kernel/FileSystem/InodeFile.cpp index b09e7df66f..9825db4859 100644 --- a/Kernel/FileSystem/InodeFile.cpp +++ b/Kernel/FileSystem/InodeFile.cpp @@ -80,11 +80,11 @@ ErrorOr InodeFile::ioctl(OpenFileDescription& description, unsigned reques } } -ErrorOr> InodeFile::vmobject_for_mmap(Process&, Memory::VirtualRange const&, u64&, bool shared) +ErrorOr> InodeFile::vmobject_for_mmap(Process&, Memory::VirtualRange const& range, u64& offset, bool shared) { if (shared) - return TRY(Memory::SharedInodeVMObject::try_create_with_inode(inode())); - return TRY(Memory::PrivateInodeVMObject::try_create_with_inode(inode())); + return TRY(Memory::SharedInodeVMObject::try_create_with_inode_and_range(inode(), offset, range.size())); + return TRY(Memory::PrivateInodeVMObject::try_create_with_inode_and_range(inode(), offset, range.size())); } ErrorOr> InodeFile::pseudo_path(OpenFileDescription const&) const diff --git a/Kernel/Memory/PrivateInodeVMObject.cpp b/Kernel/Memory/PrivateInodeVMObject.cpp index 314ee5351b..6f769c6df5 100644 --- a/Kernel/Memory/PrivateInodeVMObject.cpp +++ b/Kernel/Memory/PrivateInodeVMObject.cpp @@ -11,7 +11,18 @@ namespace Kernel::Memory { ErrorOr> PrivateInodeVMObject::try_create_with_inode(Inode& inode) { - auto new_physical_pages = TRY(VMObject::try_create_physical_pages(inode.size())); + if (inode.size() == 0) + return EINVAL; + return try_create_with_inode_and_range(inode, 0, inode.size()); +} + +ErrorOr> PrivateInodeVMObject::try_create_with_inode_and_range(Inode& inode, u64 offset, size_t range_size) +{ + // Note: To ensure further allocation of a Region with this VMObject will not complain + // on "smaller" VMObject than the requested Region, we simply take the max size between both values. + auto size = max(inode.size(), (offset + range_size)); + VERIFY(size > 0); + auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); auto dirty_pages = TRY(Bitmap::try_create(new_physical_pages.size(), false)); return adopt_nonnull_lock_ref_or_enomem(new (nothrow) PrivateInodeVMObject(inode, move(new_physical_pages), move(dirty_pages))); } diff --git a/Kernel/Memory/PrivateInodeVMObject.h b/Kernel/Memory/PrivateInodeVMObject.h index c40b6d45c8..9542bf1b78 100644 --- a/Kernel/Memory/PrivateInodeVMObject.h +++ b/Kernel/Memory/PrivateInodeVMObject.h @@ -18,6 +18,7 @@ public: virtual ~PrivateInodeVMObject() override; static ErrorOr> try_create_with_inode(Inode&); + static ErrorOr> try_create_with_inode_and_range(Inode&, u64 offset, size_t range_size); virtual ErrorOr> try_clone() override; private: diff --git a/Kernel/Memory/SharedInodeVMObject.cpp b/Kernel/Memory/SharedInodeVMObject.cpp index aa78cd2aac..4cf5475cb4 100644 --- a/Kernel/Memory/SharedInodeVMObject.cpp +++ b/Kernel/Memory/SharedInodeVMObject.cpp @@ -12,7 +12,17 @@ namespace Kernel::Memory { ErrorOr> SharedInodeVMObject::try_create_with_inode(Inode& inode) { - size_t size = inode.size(); + if (inode.size() == 0) + return EINVAL; + return try_create_with_inode_and_range(inode, 0, inode.size()); +} + +ErrorOr> SharedInodeVMObject::try_create_with_inode_and_range(Inode& inode, u64 offset, size_t range_size) +{ + // Note: To ensure further allocation of a Region with this VMObject will not complain + // on "smaller" VMObject than the requested Region, we simply take the max size between both values. + auto size = max(inode.size(), (offset + range_size)); + VERIFY(size > 0); if (auto shared_vmobject = inode.shared_vmobject()) return shared_vmobject.release_nonnull(); auto new_physical_pages = TRY(VMObject::try_create_physical_pages(size)); diff --git a/Kernel/Memory/SharedInodeVMObject.h b/Kernel/Memory/SharedInodeVMObject.h index 8a9ee21c46..d1f859864e 100644 --- a/Kernel/Memory/SharedInodeVMObject.h +++ b/Kernel/Memory/SharedInodeVMObject.h @@ -16,6 +16,7 @@ class SharedInodeVMObject final : public InodeVMObject { public: static ErrorOr> try_create_with_inode(Inode&); + static ErrorOr> try_create_with_inode_and_range(Inode&, u64 offset, size_t range_size); virtual ErrorOr> try_clone() override; ErrorOr sync(off_t offset_in_pages = 0, size_t pages = -1);