diff options
author | Andreas Kling <awesomekling@gmail.com> | 2020-01-07 15:53:42 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2020-01-07 15:53:42 +0100 |
commit | 5387a192689ba61b4cc5002be91fb6779534b8b2 (patch) | |
tree | 6df46d380faef481e3bdd68acc2a406326cab765 | |
parent | a47f3031ae642a9297eca6a39a805ecbddc1a7ba (diff) | |
download | serenity-5387a192689ba61b4cc5002be91fb6779534b8b2.zip |
Kernel: Make Process::file_description() vend a RefPtr<FileDescription>
This encourages callers to strongly reference file descriptions while
working with them.
This fixes a use-after-free issue where one thread would close() an
open fd while another thread was blocked on it becoming readable.
Test: Kernel/uaf-close-while-blocked-in-read.cpp
-rw-r--r-- | Kernel/FileSystem/ProcFS.cpp | 6 | ||||
-rw-r--r-- | Kernel/Process.cpp | 73 | ||||
-rw-r--r-- | Kernel/Process.h | 3 | ||||
-rw-r--r-- | Tests/Kernel/uaf-close-while-blocked-in-read.cpp | 30 |
4 files changed, 66 insertions, 46 deletions
diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 95dd14447c..79cf40e494 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -218,7 +218,7 @@ Optional<KBuffer> procfs$pid_fds(InodeIdentifier identifier) } for (int i = 0; i < process.max_open_file_descriptors(); ++i) { - auto* description = process.file_description(i); + auto description = process.file_description(i); if (!description) continue; bool cloexec = process.fd_flags(i) & FD_CLOEXEC; @@ -245,7 +245,7 @@ Optional<KBuffer> procfs$pid_fd_entry(InodeIdentifier identifier) return {}; auto& process = handle->process(); int fd = to_fd(identifier); - auto* description = process.file_description(fd); + auto description = process.file_description(fd); if (!description) return {}; return description->absolute_path().to_byte_buffer(); @@ -1191,7 +1191,7 @@ bool ProcFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&) return false; auto& process = handle->process(); for (int i = 0; i < process.max_open_file_descriptors(); ++i) { - auto* description = process.file_description(i); + auto description = process.file_description(i); if (!description) continue; char name[16]; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 67a8be2353..afbd8a3561 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -326,7 +326,7 @@ void* Process::sys$mmap(const Syscall::SC_mmap_params* user_params) return (void*)-EINVAL; if (static_cast<size_t>(offset) & ~PAGE_MASK) return (void*)-EINVAL; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return (void*)-EBADF; auto region_or_error = description->mmap(*this, VirtualAddress((u32)addr), static_cast<size_t>(offset), size, prot); @@ -1208,16 +1208,7 @@ Process* Process::from_pid(pid_t pid) return nullptr; } -FileDescription* Process::file_description(int fd) -{ - if (fd < 0) - return nullptr; - if (fd < m_fds.size()) - return m_fds[fd].description.ptr(); - return nullptr; -} - -const FileDescription* Process::file_description(int fd) const +RefPtr<FileDescription> Process::file_description(int fd) const { if (fd < 0) return nullptr; @@ -1241,7 +1232,7 @@ ssize_t Process::sys$get_dir_entries(int fd, void* buffer, ssize_t size) return -EINVAL; if (!validate_write(buffer, size)) return -EFAULT; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; return description->get_dir_entries((u8*)buffer, size); @@ -1249,7 +1240,7 @@ ssize_t Process::sys$get_dir_entries(int fd, void* buffer, ssize_t size) int Process::sys$lseek(int fd, off_t offset, int whence) { - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; return description->seek(offset, whence); @@ -1261,7 +1252,7 @@ int Process::sys$ttyname_r(int fd, char* buffer, ssize_t size) return -EINVAL; if (!validate_write(buffer, size)) return -EFAULT; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; if (!description->is_tty()) @@ -1279,7 +1270,7 @@ int Process::sys$ptsname_r(int fd, char* buffer, ssize_t size) return -EINVAL; if (!validate_write(buffer, size)) return -EFAULT; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; auto* master_pty = description->master_pty(); @@ -1312,7 +1303,7 @@ ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count) return -EINVAL; } - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; @@ -1388,7 +1379,7 @@ ssize_t Process::sys$write(int fd, const u8* data, ssize_t size) #ifdef DEBUG_IO dbgprintf("%s(%u): sys$write(%d, %p, %u)\n", name().characters(), pid(), fd, data, size); #endif - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; if (!description->is_writable()) @@ -1408,7 +1399,7 @@ ssize_t Process::sys$read(int fd, u8* buffer, ssize_t size) #ifdef DEBUG_IO dbgprintf("%s(%u) sys$read(%d, %p, %u)\n", name().characters(), pid(), fd, buffer, size); #endif - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; if (!description->is_readable()) @@ -1426,7 +1417,7 @@ ssize_t Process::sys$read(int fd, u8* buffer, ssize_t size) int Process::sys$close(int fd) { - auto* description = file_description(fd); + auto description = file_description(fd); #ifdef DEBUG_IO dbgprintf("%s(%u) sys$close(%d) %p\n", name().characters(), pid(), fd, description); #endif @@ -1467,7 +1458,7 @@ int Process::sys$fcntl(int fd, int cmd, u32 arg) (void)cmd; (void)arg; dbgprintf("sys$fcntl: fd=%d, cmd=%d, arg=%u\n", fd, cmd, arg); - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; // NOTE: The FD flags are not shared between FileDescription objects. @@ -1503,7 +1494,7 @@ int Process::sys$fstat(int fd, stat* statbuf) { if (!validate_write_typed(statbuf)) return -EFAULT; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; return description->fstat(*statbuf); @@ -1587,7 +1578,7 @@ int Process::sys$chdir(const char* user_path, size_t path_length) int Process::sys$fchdir(int fd) { - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; @@ -1684,7 +1675,7 @@ int Process::sys$openat(const Syscall::SC_openat_params* user_params) if (dirfd == AT_FDCWD) { base = current_directory(); } else { - auto* base_description = file_description(dirfd); + auto base_description = file_description(dirfd); if (!base_description) return -EBADF; if (!base_description->is_directory()) @@ -2184,7 +2175,7 @@ int Process::sys$setpgid(pid_t specified_pid, pid_t specified_pgid) int Process::sys$ioctl(int fd, unsigned request, unsigned arg) { - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; SmapDisabler disabler; @@ -2198,7 +2189,7 @@ int Process::sys$getdtablesize() int Process::sys$dup(int old_fd) { - auto* description = file_description(old_fd); + auto description = file_description(old_fd); if (!description) return -EBADF; int new_fd = alloc_fd(0); @@ -2210,7 +2201,7 @@ int Process::sys$dup(int old_fd) int Process::sys$dup2(int old_fd, int new_fd) { - auto* description = file_description(old_fd); + auto description = file_description(old_fd); if (!description) return -EBADF; if (new_fd < 0 || new_fd >= m_max_open_file_descriptors) @@ -2436,7 +2427,7 @@ int Process::sys$select(const Syscall::SC_select_params* params) return; FD_ZERO(fds); for (int fd : vector) { - if (auto* description = file_description(fd); description && should_mark(*description)) { + if (auto description = file_description(fd); description && should_mark(*description)) { FD_SET(fd, fds); ++marked_fd_count; } @@ -2493,7 +2484,7 @@ int Process::sys$poll(pollfd* fds, int nfds, int timeout) int fds_with_revents = 0; for (int i = 0; i < nfds; ++i) { - auto* description = file_description(fds[i].fd); + auto description = file_description(fds[i].fd); if (!description) { fds[i].revents = POLLNVAL; continue; @@ -2580,7 +2571,7 @@ int Process::sys$chmod(const char* user_path, size_t path_length, mode_t mode) int Process::sys$fchmod(int fd, mode_t mode) { SmapDisabler disabler; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; return description->chmod(mode); @@ -2589,7 +2580,7 @@ int Process::sys$fchmod(int fd, mode_t mode) int Process::sys$fchown(int fd, uid_t uid, gid_t gid) { SmapDisabler disabler; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; return description->chown(uid, gid); @@ -2755,7 +2746,7 @@ int Process::sys$bind(int sockfd, const sockaddr* address, socklen_t address_len { if (!validate_read(address, address_length)) return -EFAULT; - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -2767,7 +2758,7 @@ int Process::sys$bind(int sockfd, const sockaddr* address, socklen_t address_len int Process::sys$listen(int sockfd, int backlog) { - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -2788,7 +2779,7 @@ int Process::sys$accept(int accepting_socket_fd, sockaddr* address, socklen_t* a int accepted_socket_fd = alloc_fd(); if (accepted_socket_fd < 0) return accepted_socket_fd; - auto* accepting_socket_description = file_description(accepting_socket_fd); + auto accepting_socket_description = file_description(accepting_socket_fd); if (!accepting_socket_description) return -EBADF; if (!accepting_socket_description->is_socket()) @@ -2826,7 +2817,7 @@ int Process::sys$connect(int sockfd, const sockaddr* address, socklen_t address_ int fd = alloc_fd(); if (fd < 0) return fd; - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -2855,7 +2846,7 @@ ssize_t Process::sys$sendto(const Syscall::SC_sendto_params* params) return -EFAULT; if (addr && !validate_read(addr, addr_length)) return -EFAULT; - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -2887,7 +2878,7 @@ ssize_t Process::sys$recvfrom(const Syscall::SC_recvfrom_params* params) } else if (addr) { return -EINVAL; } - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -2917,7 +2908,7 @@ int Process::sys$getsockname(int sockfd, sockaddr* addr, socklen_t* addrlen) if (!validate_write(addr, *addrlen)) return -EFAULT; - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; @@ -2944,7 +2935,7 @@ int Process::sys$getpeername(int sockfd, sockaddr* addr, socklen_t* addrlen) if (!validate_write(addr, *addrlen)) return -EFAULT; - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; @@ -3027,7 +3018,7 @@ int Process::sys$getsockopt(const Syscall::SC_getsockopt_params* params) return -EFAULT; if (!validate_write(value, *value_size)) return -EFAULT; - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -3051,7 +3042,7 @@ int Process::sys$setsockopt(const Syscall::SC_setsockopt_params* params) if (!validate_read(value, value_size)) return -EFAULT; - auto* description = file_description(sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -3426,7 +3417,7 @@ int Process::sys$ftruncate(int fd, off_t length) { if (length < 0) return -EINVAL; - auto* description = file_description(fd); + auto description = file_description(fd); if (!description) return -EBADF; if (!description->is_writable()) diff --git a/Kernel/Process.h b/Kernel/Process.h index 9c8f86a47c..9488546b89 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -81,8 +81,7 @@ public: bool in_group(gid_t) const; - FileDescription* file_description(int fd); - const FileDescription* file_description(int fd) const; + RefPtr<FileDescription> file_description(int fd) const; int fd_flags(int fd) const; template<typename Callback> diff --git a/Tests/Kernel/uaf-close-while-blocked-in-read.cpp b/Tests/Kernel/uaf-close-while-blocked-in-read.cpp new file mode 100644 index 0000000000..b75e0bbbaa --- /dev/null +++ b/Tests/Kernel/uaf-close-while-blocked-in-read.cpp @@ -0,0 +1,30 @@ +#include <pthread.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int pipefds[2]; + +int main(int, char**) +{ + pipe(pipefds); + + pthread_t tid; + pthread_create( + &tid, nullptr, [](void*) -> void* { + sleep(1); + printf("Second thread closing pipes!\n"); + close(pipefds[0]); + close(pipefds[1]); + pthread_exit(nullptr); + return nullptr; + }, + nullptr); + + printf("First thread doing a blocking read from pipe...\n"); + char buffer[16]; + int nread = read(pipefds[0], buffer, sizeof(buffer)); + printf("Ok, read %d bytes from pipe\n", nread); + + return 0; +} |