1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 07:48:11 +00:00

Kernel: Fix incorrect EFAULTs when syscall would write into COW pages.

This commit is contained in:
Andreas Kling 2019-01-25 01:39:15 +01:00
parent a4a106a430
commit 11b73c38d8
4 changed files with 38 additions and 32 deletions

View file

@ -208,7 +208,18 @@ Region* MemoryManager::region_from_laddr(Process& process, LinearAddress laddr)
if (region->contains(laddr)) if (region->contains(laddr))
return region.ptr(); return region.ptr();
} }
kprintf("%s(%u) Couldn't find region for L%x (CR3=%x)\n", process.name().characters(), process.pid(), laddr.get(), process.page_directory().cr3()); dbgprintf("%s(%u) Couldn't find region for L%x (CR3=%x)\n", process.name().characters(), process.pid(), laddr.get(), process.page_directory().cr3());
return nullptr;
}
const Region* MemoryManager::region_from_laddr(const Process& process, LinearAddress laddr)
{
// FIXME: Use a binary search tree (maybe red/black?) or some other more appropriate data structure!
for (auto& region : process.m_regions) {
if (region->contains(laddr))
return region.ptr();
}
dbgprintf("%s(%u) Couldn't find region for L%x (CR3=%x)\n", process.name().characters(), process.pid(), laddr.get(), process.page_directory().cr3());
return nullptr; return nullptr;
} }
@ -515,34 +526,14 @@ bool MemoryManager::map_region(Process& process, Region& region)
bool MemoryManager::validate_user_read(const Process& process, LinearAddress laddr) const bool MemoryManager::validate_user_read(const Process& process, LinearAddress laddr) const
{ {
dword pageDirectoryIndex = (laddr.get() >> 22) & 0x3ff; auto* region = region_from_laddr(process, laddr);
dword pageTableIndex = (laddr.get() >> 12) & 0x3ff; return region && region->is_readable();
auto pde = PageDirectoryEntry(&const_cast<Process&>(process).page_directory().entries()[pageDirectoryIndex]);
if (!pde.is_present())
return false;
auto pte = PageTableEntry(&pde.pageTableBase()[pageTableIndex]);
if (!pte.is_present())
return false;
if (process.isRing3() && !pte.is_user_allowed())
return false;
return true;
} }
bool MemoryManager::validate_user_write(const Process& process, LinearAddress laddr) const bool MemoryManager::validate_user_write(const Process& process, LinearAddress laddr) const
{ {
dword pageDirectoryIndex = (laddr.get() >> 22) & 0x3ff; auto* region = region_from_laddr(process, laddr);
dword pageTableIndex = (laddr.get() >> 12) & 0x3ff; return region && region->is_writable();
auto pde = PageDirectoryEntry(&const_cast<Process&>(process).page_directory().entries()[pageDirectoryIndex]);
if (!pde.is_present())
return false;
auto pte = PageTableEntry(&pde.pageTableBase()[pageTableIndex]);
if (!pte.is_present())
return false;
if (process.isRing3() && !pte.is_user_allowed())
return false;
if (!pte.is_writable())
return false;
return true;
} }
RetainPtr<Region> Region::clone() RetainPtr<Region> Region::clone()

View file

@ -246,6 +246,7 @@ private:
void remove_identity_mapping(PageDirectory&, LinearAddress, size_t); void remove_identity_mapping(PageDirectory&, LinearAddress, size_t);
static Region* region_from_laddr(Process&, LinearAddress); static Region* region_from_laddr(Process&, LinearAddress);
static const Region* region_from_laddr(const Process&, LinearAddress);
bool copy_on_write(Region&, unsigned page_index_in_region); bool copy_on_write(Region&, unsigned page_index_in_region);
bool page_in_from_inode(Region&, unsigned page_index_in_region); bool page_in_from_inode(Region&, unsigned page_index_in_region);

View file

@ -1605,20 +1605,34 @@ bool Process::validate_read_from_kernel(LinearAddress laddr) const
bool Process::validate_read(const void* address, size_t size) const bool Process::validate_read(const void* address, size_t size) const
{ {
if ((reinterpret_cast<dword>(address) & PAGE_MASK) != ((reinterpret_cast<dword>(address) + (size - 1)) & PAGE_MASK)) { if (isRing0())
if (!MM.validate_user_read(*this, LinearAddress((dword)address).offset(size))) return true;
ASSERT(size);
if (!size)
return false;
LinearAddress first_address((dword)address);
LinearAddress last_address = first_address.offset(size - 1);
if (first_address.page_base() != last_address.page_base()) {
if (!MM.validate_user_read(*this, last_address))
return false; return false;
} }
return MM.validate_user_read(*this, LinearAddress((dword)address)); return MM.validate_user_read(*this, first_address);
} }
bool Process::validate_write(void* address, size_t size) const bool Process::validate_write(void* address, size_t size) const
{ {
if ((reinterpret_cast<dword>(address) & PAGE_MASK) != ((reinterpret_cast<dword>(address) + (size - 1)) & PAGE_MASK)) { if (isRing0())
if (!MM.validate_user_write(*this, LinearAddress((dword)address).offset(size))) return true;
ASSERT(size);
if (!size)
return false;
LinearAddress first_address((dword)address);
LinearAddress last_address = first_address.offset(size - 1);
if (first_address.page_base() != last_address.page_base()) {
if (!MM.validate_user_write(*this, last_address))
return false; return false;
} }
return MM.validate_user_write(*this, LinearAddress((dword)address)); return MM.validate_user_write(*this, last_address);
} }
pid_t Process::sys$getsid(pid_t pid) pid_t Process::sys$getsid(pid_t pid)

View file

@ -90,7 +90,7 @@ int main(int, char**)
} }
if (FD_ISSET(ptm_fd, &rfds)) { if (FD_ISSET(ptm_fd, &rfds)) {
byte buffer[1024]; byte buffer[4096];
ssize_t nread = read(ptm_fd, buffer, sizeof(buffer)); ssize_t nread = read(ptm_fd, buffer, sizeof(buffer));
if (nread < 0) { if (nread < 0) {
dbgprintf("Terminal read error: %s\n", strerror(errno)); dbgprintf("Terminal read error: %s\n", strerror(errno));