Kernel: Allow pinning multiple memory regions

One port allocated memory page by page and did a big write on the final
multi-region buffer. Now we allow this!
This commit is contained in:
2026-08-25 15:33:56 +03:00
parent 645150039a
commit 59077f0f72
2 changed files with 89 additions and 91 deletions
+1 -1
View File
@@ -286,7 +286,7 @@ namespace Kernel
BAN::ErrorOr<FileParent> find_parent_file(int fd, const char* path, int flags) const;
BAN::ErrorOr<VirtualFileSystem::File> find_relative_parent(int fd, const char* path) const;
BAN::ErrorOr<MemoryRegion*> validate_and_pin_pointer_access(const void*, size_t, bool needs_write);
BAN::ErrorOr<void> validate_and_pin_pointer_access(const void*, size_t, bool needs_write, BAN::Vector<MemoryRegion*>&);
uint64_t signal_pending_mask() const
{
+88 -90
View File
@@ -1262,18 +1262,12 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL);
}
MemoryRegion* value_region = nullptr;
MemoryRegion* ovalue_region = nullptr;
BAN::ScopeGuard _([&] {
if (value_region)
value_region->unpin();
if (ovalue_region)
ovalue_region->unpin();
});
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
value_region = TRY(validate_and_pin_pointer_access(value, sizeof(itimerval), false));
TRY(validate_and_pin_pointer_access(value, sizeof(itimerval), false, regions));
if (ovalue != nullptr)
ovalue_region = TRY(validate_and_pin_pointer_access(ovalue, sizeof(itimerval), true));
TRY(validate_and_pin_pointer_access(ovalue, sizeof(itimerval), true, regions));
{
SpinLockGuard _(s_process_lock);
@@ -1467,8 +1461,10 @@ namespace Kernel
return 0;
}
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, true));
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(buffer, count, true, regions));
return TRY(m_open_file_descriptors.read(fd, BAN::ByteSpan(static_cast<uint8_t*>(buffer), count)));
}
@@ -1480,8 +1476,10 @@ namespace Kernel
return 0;
}
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, false));
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(buffer, count, false, regions));
return TRY(m_open_file_descriptors.write(fd, BAN::ConstByteSpan(static_cast<const uint8_t*>(buffer), count)));
}
@@ -1671,8 +1669,9 @@ namespace Kernel
{
auto inode = TRY(m_open_file_descriptors.inode_of(fd));
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, true));
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(buffer, count, true, regions));
return TRY(inode->read(offset, { reinterpret_cast<uint8_t*>(buffer), count }));
}
@@ -1681,8 +1680,9 @@ namespace Kernel
{
auto inode = TRY(m_open_file_descriptors.inode_of(fd));
auto* buffer_region = TRY(validate_and_pin_pointer_access(buffer, count, false));
BAN::ScopeGuard _([buffer_region] { buffer_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(buffer, count, false, regions));
return TRY(inode->write(offset, { reinterpret_cast<const uint8_t*>(buffer), count })); }
@@ -1702,10 +1702,7 @@ namespace Kernel
{
LockGuard _(m_process_lock);
if (!m_credentials.is_superuser() && inode->uid() != m_credentials.euid())
{
dwarnln("cannot chmod uid {} vs {}", inode->uid(), m_credentials.euid());
return BAN::Error::from_errno(EPERM);
}
}
TRY(inode->chmod(mode & ~S_IFMASK));
@@ -1877,8 +1874,9 @@ namespace Kernel
if (!inode->mode().ifsock())
return BAN::Error::from_errno(ENOTSOCK);
auto* buffer = TRY(validate_and_pin_pointer_access(user_option_value, option_len, true));
BAN::ScopeGuard _([buffer] { buffer->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(user_option_value, option_len, true, regions));
TRY(inode->getsockopt(level, option_name, user_option_value, &option_len));
TRY(write_to_user(user_option_len, &option_len, sizeof(socklen_t)));
@@ -1895,8 +1893,9 @@ namespace Kernel
if (!inode->mode().ifsock())
return BAN::Error::from_errno(ENOTSOCK);
auto* buffer = TRY(validate_and_pin_pointer_access(user_option_value, option_len, false));
BAN::ScopeGuard _([buffer] { buffer->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(user_option_value, option_len, false, regions));
TRY(inode->setsockopt(level, option_name, user_option_value, option_len));
@@ -1910,20 +1909,13 @@ namespace Kernel
if (flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC))
return BAN::Error::from_errno(EINVAL);
MemoryRegion* address_region1 = nullptr;
MemoryRegion* address_region2 = nullptr;
BAN::ScopeGuard _([&] {
if (address_region1)
address_region1->unpin();
if (address_region2)
address_region2->unpin();
});
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
if (address_len)
{
address_region1 = TRY(validate_and_pin_pointer_access(address_len, sizeof(address_len), true));
address_region2 = TRY(validate_and_pin_pointer_access(address, *address_len, true));
TRY(validate_and_pin_pointer_access(address_len, sizeof(address_len), true, regions));
TRY(validate_and_pin_pointer_access(address, *address_len, true, regions));
}
auto inode = TRY(m_open_file_descriptors.inode_of(socket));
@@ -1990,23 +1982,17 @@ namespace Kernel
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
BAN::Vector<MemoryRegion*> regions;
TRY(regions.reserve(!!message.msg_name + !!message.msg_control + !!message.msg_iov));
BAN::ScopeGuard _([&regions] {
for (auto* region : regions)
region->unpin();
});
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
if (message.msg_name)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, true))));
TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, true, regions));
if (message.msg_control)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_control, message.msg_controllen, true))));
TRY(validate_and_pin_pointer_access(message.msg_control, message.msg_controllen, true, regions));
if (message.msg_iov)
{
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), true))));
TRY(regions.reserve(regions.size() + message.msg_iovlen));
TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), true, regions));
for (int i = 0; i < message.msg_iovlen; i++)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, true))));
TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, true, regions));
}
const auto ret = TRY(m_open_file_descriptors.recvmsg(socket, message, flags));
@@ -2022,23 +2008,18 @@ namespace Kernel
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
BAN::Vector<MemoryRegion*> regions;
TRY(regions.reserve(!!message.msg_name + !!message.msg_control + !!message.msg_iov));
BAN::ScopeGuard _([&regions] {
for (auto* region : regions)
region->unpin();
});
BAN::ScopeGuard _([&regions] { for (auto* region : regions) region->unpin(); });
if (message.msg_name)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, false))));
TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, false, regions));
if (message.msg_control)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_control, message.msg_controllen, false))));
TRY(validate_and_pin_pointer_access(message.msg_control, message.msg_controllen, false, regions));
if (message.msg_iov)
{
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), false))));
TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), false, regions));
TRY(regions.reserve(regions.size() + message.msg_iovlen));
for (int i = 0; i < message.msg_iovlen; i++)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, false))));
TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, false, regions));
}
return TRY(m_open_file_descriptors.sendmsg(socket, message, flags));
@@ -2173,8 +2154,9 @@ namespace Kernel
BAN::ErrorOr<long> Process::sys_ppoll(pollfd* fds, nfds_t nfds, const timespec* user_timeout, const sigset_t* user_sigmask)
{
auto* fds_region = TRY(validate_and_pin_pointer_access(fds, nfds * sizeof(pollfd), true));
BAN::ScopeGuard _([fds_region] { fds_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(fds, nfds * sizeof(pollfd), true, regions));
const auto old_sigmask = Thread::current().m_signal_block_mask;
if (user_sigmask != nullptr)
@@ -2356,8 +2338,9 @@ namespace Kernel
timeout.tv_nsec;
}
auto* events_region = TRY(validate_and_pin_pointer_access(events, maxevents * sizeof(epoll_event), true));
BAN::ScopeGuard _([events_region] { events_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(events, maxevents * sizeof(epoll_event), true, regions));
const auto old_sigmask = Thread::current().m_signal_block_mask;
if (user_sigmask)
@@ -2570,8 +2553,10 @@ namespace Kernel
if (BAN::Math::will_multiplication_overflow(list_len, sizeof(struct dirent)))
return BAN::Error::from_errno(EOVERFLOW);
auto* list_region = TRY(validate_and_pin_pointer_access(list, list_len * sizeof(struct dirent), true));
BAN::ScopeGuard _([list_region] { list_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(list, list_len * sizeof(struct dirent), true, regions));
return TRY(m_open_file_descriptors.read_dir_entries(fd, list, list_len));
}
@@ -3408,8 +3393,9 @@ namespace Kernel
const bool is_private = (op & FUTEX_PRIVATE);
op &= ~(FUTEX_PRIVATE | FUTEX_REALTIME);
auto* buffer_region = TRY(validate_and_pin_pointer_access(addr, sizeof(uint32_t), false));
BAN::ScopeGuard pin_guard([buffer_region] { buffer_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _0([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(addr, sizeof(uint32_t), false, regions));
const paddr_t paddr = m_page_table->physical_address_of(vaddr & PAGE_ADDR_MASK) | (vaddr & ~PAGE_ADDR_MASK);
ASSERT(paddr != 0);
@@ -3470,7 +3456,7 @@ namespace Kernel
}
}
LockGuard _(futex->mutex);
LockGuard _1(futex->mutex);
switch (op)
{
@@ -3553,8 +3539,9 @@ namespace Kernel
if (stack_vaddr % PAGE_SIZE || stack_size % PAGE_SIZE)
return BAN::Error::from_errno(EINVAL);
auto* memory_region = TRY(validate_and_pin_pointer_access(stack_base, stack_size, true));
BAN::ScopeGuard _0([memory_region] { if (memory_region) memory_region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _0([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(stack_base, stack_size, true, regions));
const vaddr_t initial_stack_pointer = stack_vaddr + stack_size - sizeof(void*);
*reinterpret_cast<void**>(initial_stack_pointer) = arg;
@@ -4022,13 +4009,14 @@ namespace Kernel
if (BAN::Math::will_multiplication_overflow(count, sizeof(gid_t)))
return BAN::Error::from_errno(EOVERFLOW);
LockGuard _(m_process_lock);
LockGuard _0(m_process_lock);
if (!m_credentials.is_superuser())
return BAN::Error::from_errno(EPERM);
auto* region = TRY(validate_and_pin_pointer_access(groups, count * sizeof(gid_t), false));
BAN::ScopeGuard pin_guard([region] { region->unpin(); });
BAN::Vector<MemoryRegion*> regions;
BAN::ScopeGuard _1([&] { for (auto* region : regions) region->unpin(); });
TRY(validate_and_pin_pointer_access(groups, count * sizeof(gid_t), false, regions));
TRY(m_credentials.set_groups({ groups, count }));
@@ -4093,26 +4081,23 @@ namespace Kernel
return region->allocate_page_containing(address, wants_write);
}
BAN::ErrorOr<MemoryRegion*> Process::validate_and_pin_pointer_access(const void* ptr, size_t size, bool needs_write)
BAN::ErrorOr<void> Process::validate_and_pin_pointer_access(const void* ptr, size_t size, bool needs_write, BAN::Vector<MemoryRegion*>& regions)
{
// TODO: allow pinning multiple regions?
const vaddr_t user_vaddr = reinterpret_cast<vaddr_t>(ptr);
vaddr_t vaddr = reinterpret_cast<vaddr_t>(ptr);
{
RWLockRDGuard _(m_memory_region_lock);
const size_t first_index = find_mapped_region(user_vaddr);
const size_t first_index = find_mapped_region(vaddr);
for (size_t i = first_index; i < m_mapped_regions.size(); i++)
{
auto& region = m_mapped_regions[i];
if (user_vaddr >= region->vaddr() + region->size())
const auto& region = m_mapped_regions[i];
if (vaddr < region->vaddr())
break;
if (!region->contains_fully(user_vaddr, size))
continue;
const size_t page_count = range_page_count(user_vaddr, size);
const vaddr_t page_base = user_vaddr & PAGE_ADDR_MASK;
const vaddr_t min_vaddr_end = BAN::Math::min(vaddr + size, region->vaddr() + region->size());
const size_t page_count = range_page_count(vaddr, min_vaddr_end - vaddr);
const vaddr_t page_base = vaddr & PAGE_ADDR_MASK;
for (size_t p = 0; p < page_count; p++)
{
const auto flags = PageTable::UserSupervisor | (needs_write ? PageTable::ReadWrite : 0) | PageTable::Present;
@@ -4120,8 +4105,15 @@ namespace Kernel
goto validate_and_pin_pointer_access_with_allocation;
}
TRY(regions.push_back(region.ptr()));
region->pin();
return region.ptr();
if (region->contains(vaddr + size - 1))
return {};
const vaddr_t next_vaddr = region->vaddr() + region->size();
size -= next_vaddr - vaddr;
vaddr = next_vaddr;
}
return BAN::Error::from_errno(EFAULT);
@@ -4130,17 +4122,16 @@ namespace Kernel
validate_and_pin_pointer_access_with_allocation:
RWLockWRGuard _(m_memory_region_lock);
const size_t first_index = find_mapped_region(user_vaddr);
const size_t first_index = find_mapped_region(vaddr);
for (size_t i = first_index; i < m_mapped_regions.size(); i++)
{
auto& region = m_mapped_regions[i];
if (user_vaddr >= region->vaddr() + region->size())
if (vaddr < region->vaddr())
break;
if (!region->contains_fully(user_vaddr, size))
continue;
const size_t page_count = range_page_count(user_vaddr, size);
const vaddr_t page_base = user_vaddr & PAGE_ADDR_MASK;
const vaddr_t min_vaddr_end = BAN::Math::min(vaddr + size, region->vaddr() + region->size());
const size_t page_count = range_page_count(vaddr, min_vaddr_end - vaddr);
const vaddr_t page_base = vaddr & PAGE_ADDR_MASK;
for (size_t p = 0; p < page_count; p++)
{
const auto flags = PageTable::UserSupervisor | (needs_write ? PageTable::ReadWrite : 0) | PageTable::Present;
@@ -4150,8 +4141,15 @@ namespace Kernel
return BAN::Error::from_errno(EFAULT);
}
TRY(regions.push_back(region.ptr()));
region->pin();
return region.ptr();
if (region->contains(vaddr + size - 1))
return {};
const vaddr_t next_vaddr = region->vaddr() + region->size();
size -= next_vaddr - vaddr;
vaddr = next_vaddr;
}
return BAN::Error::from_errno(EFAULT);