From b36a2d66865ae8205b6a7589b8566f27746002f4 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 16 Sep 2020 11:45:00 -0400 Subject: [PATCH] Kernel+LibC+UserspaceEmulator: Mostly add recvmsg(), sendmsg() The implementation only supports a single iovec for now. Some might say having more than one iovec is the main point of recvmsg() and sendmsg(), but I'm interested in the control message bits. --- DevTools/UserspaceEmulator/Emulator.cpp | 80 +++++++++++++++---------- DevTools/UserspaceEmulator/Emulator.h | 4 +- Kernel/API/Syscall.h | 20 +------ Kernel/Process.h | 4 +- Kernel/StdLib.h | 6 ++ Kernel/Syscalls/socket.cpp | 62 ++++++++++++------- Kernel/UnixTypes.h | 11 ++++ Libraries/LibC/sys/socket.cpp | 37 ++++++++++-- Libraries/LibC/sys/socket.h | 13 ++++ 9 files changed, 158 insertions(+), 79 deletions(-) diff --git a/DevTools/UserspaceEmulator/Emulator.cpp b/DevTools/UserspaceEmulator/Emulator.cpp index 657a8fa390b..f523758f7cc 100644 --- a/DevTools/UserspaceEmulator/Emulator.cpp +++ b/DevTools/UserspaceEmulator/Emulator.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -354,10 +355,10 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) return virt$listen(arg1, arg2); case SC_select: return virt$select(arg1); - case SC_sendto: - return virt$sendto(arg1); - case SC_recvfrom: - return virt$recvfrom(arg1); + case SC_recvmsg: + return virt$recvmsg(arg1, arg2, arg3); + case SC_sendmsg: + return virt$sendmsg(arg1, arg2, arg3); case SC_kill: return virt$kill(arg1, arg2); case SC_set_mmap_name: @@ -593,49 +594,66 @@ int Emulator::virt$socket(int domain, int type, int protocol) return syscall(SC_socket, domain, type, protocol); } -int Emulator::virt$recvfrom(FlatPtr params_addr) +int Emulator::virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags) { - Syscall::SC_recvfrom_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - auto buffer = ByteBuffer::create_uninitialized(params.buffer.size); + msghdr mmu_msg; + mmu().copy_from_vm(&mmu_msg, msg_addr, sizeof(mmu_msg)); - if (!params.addr_length && params.addr) - return -EINVAL; + if (mmu_msg.msg_iovlen != 1) + return -ENOTSUP; // FIXME: Support this :) + iovec mmu_iov; + mmu().copy_from_vm(&mmu_iov, (FlatPtr)mmu_msg.msg_iov, sizeof(mmu_iov)); + auto buffer = ByteBuffer::create_uninitialized(mmu_iov.iov_len); - socklen_t address_length = 0; - if (params.addr_length) - mmu().copy_from_vm(&address_length, (FlatPtr)params.addr_length, sizeof(address_length)); + ByteBuffer control_buffer; + if (mmu_msg.msg_control) + control_buffer = ByteBuffer::create_uninitialized(mmu_msg.msg_controllen); - sockaddr_storage address; - if (params.addr) - mmu().copy_from_vm(&address, (FlatPtr)params.addr, min(sizeof(address), (size_t)address_length)); - - int rc = recvfrom(params.sockfd, buffer.data(), buffer.size(), params.flags, params.addr ? (struct sockaddr*)&address : nullptr, params.addr_length ? &address_length : nullptr); + sockaddr_storage addr; + iovec iov = { buffer.data(), buffer.size() }; + msghdr msg = { &addr, sizeof(addr), &iov, 1, mmu_msg.msg_control ? control_buffer.data() : nullptr, mmu_msg.msg_controllen, mmu_msg.msg_flags }; + int rc = recvmsg(sockfd, &msg, flags); if (rc < 0) return -errno; - mmu().copy_to_vm((FlatPtr)params.buffer.data, buffer.data(), buffer.size()); - - if (params.addr) - mmu().copy_to_vm((FlatPtr)params.addr, &address, address_length); - if (params.addr_length) - mmu().copy_to_vm((FlatPtr)params.addr_length, &address_length, sizeof(address_length)); + mmu().copy_to_vm((FlatPtr)mmu_iov.iov_base, buffer.data(), mmu_iov.iov_len); + if (mmu_msg.msg_name) + mmu().copy_to_vm((FlatPtr)mmu_msg.msg_name, &addr, min(sizeof(addr), (size_t)mmu_msg.msg_namelen)); + if (mmu_msg.msg_control) + mmu().copy_to_vm((FlatPtr)mmu_msg.msg_control, control_buffer.data(), min(mmu_msg.msg_controllen, msg.msg_controllen)); + mmu_msg.msg_namelen = msg.msg_namelen; + mmu_msg.msg_controllen = msg.msg_controllen; + mmu_msg.msg_flags = msg.msg_flags; + mmu().copy_to_vm(msg_addr, &mmu_msg, sizeof(mmu_msg)); return rc; } -int Emulator::virt$sendto(FlatPtr params_addr) +int Emulator::virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags) { - Syscall::SC_sendto_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); + msghdr mmu_msg; + mmu().copy_from_vm(&mmu_msg, msg_addr, sizeof(mmu_msg)); - auto buffer = mmu().copy_buffer_from_vm((FlatPtr)params.data.data, params.data.size); + if (mmu_msg.msg_iovlen != 1) + return -ENOTSUP; // FIXME: Support this :) + iovec mmu_iov; + mmu().copy_from_vm(&mmu_iov, (FlatPtr)mmu_msg.msg_iov, sizeof(mmu_iov)); + auto buffer = mmu().copy_buffer_from_vm((FlatPtr)mmu_iov.iov_base, mmu_iov.iov_len); + + ByteBuffer control_buffer; + if (mmu_msg.msg_control) + control_buffer = ByteBuffer::create_uninitialized(mmu_msg.msg_controllen); sockaddr_storage address; - if (params.addr) - mmu().copy_from_vm(&address, (FlatPtr)params.addr, min(sizeof(address), (size_t)params.addr_length)); + socklen_t address_length = 0; + if (mmu_msg.msg_name) { + address_length = min(sizeof(address), (size_t)mmu_msg.msg_namelen); + mmu().copy_from_vm(&address, (FlatPtr)mmu_msg.msg_name, address_length); + } - return sendto(params.sockfd, buffer.data(), buffer.size(), params.flags, params.addr ? (struct sockaddr*)&address : nullptr, params.addr_length); + iovec iov = { buffer.data(), buffer.size() }; + msghdr msg = { mmu_msg.msg_name ? &address : nullptr, address_length, &iov, 1, mmu_msg.msg_control ? control_buffer.data() : nullptr, mmu_msg.msg_controllen, mmu_msg.msg_flags }; + return sendmsg(sockfd, &msg, flags); } int Emulator::virt$select(FlatPtr params_addr) diff --git a/DevTools/UserspaceEmulator/Emulator.h b/DevTools/UserspaceEmulator/Emulator.h index b914ad9513a..6cef885ba4f 100644 --- a/DevTools/UserspaceEmulator/Emulator.h +++ b/DevTools/UserspaceEmulator/Emulator.h @@ -131,8 +131,8 @@ private: int virt$select(FlatPtr); int virt$accept(int sockfd, FlatPtr address, FlatPtr address_length); int virt$bind(int sockfd, FlatPtr address, socklen_t address_length); - int virt$recvfrom(FlatPtr); - int virt$sendto(FlatPtr); + int virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags); + int virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags); int virt$connect(int sockfd, FlatPtr address, socklen_t address_size); void virt$exit(int); ssize_t virt$getrandom(FlatPtr buffer, size_t buffer_size, unsigned int flags); diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index 5a95809701c..58de9dee657 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -130,8 +130,8 @@ namespace Kernel { S(fchmod) \ S(symlink) \ S(shbuf_seal) \ - S(sendto) \ - S(recvfrom) \ + S(sendmsg) \ + S(recvmsg) \ S(getsockopt) \ S(setsockopt) \ S(create_thread) \ @@ -284,22 +284,6 @@ struct SC_clock_nanosleep_params { struct timespec* remaining_sleep; }; -struct SC_sendto_params { - int sockfd; - ImmutableBufferArgument data; - int flags; - const sockaddr* addr; - socklen_t addr_length; -}; - -struct SC_recvfrom_params { - int sockfd; - MutableBufferArgument buffer; - int flags; - sockaddr* addr; - socklen_t* addr_length; -}; - struct SC_getsockopt_params { int sockfd; int level; diff --git a/Kernel/Process.h b/Kernel/Process.h index 93298e7b1a6..d84af27f699 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -290,8 +290,8 @@ public: int sys$accept(int sockfd, Userspace, Userspace); int sys$connect(int sockfd, Userspace, socklen_t); int sys$shutdown(int sockfd, int how); - ssize_t sys$sendto(Userspace); - ssize_t sys$recvfrom(Userspace); + ssize_t sys$sendmsg(int sockfd, Userspace, int flags); + ssize_t sys$recvmsg(int sockfd, Userspace, int flags); int sys$getsockopt(Userspace); int sys$setsockopt(Userspace); int sys$getsockname(Userspace); diff --git a/Kernel/StdLib.h b/Kernel/StdLib.h index 00b89129874..fb64af7ab28 100644 --- a/Kernel/StdLib.h +++ b/Kernel/StdLib.h @@ -76,6 +76,12 @@ template return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); } +template +[[nodiscard]] inline bool copy_from_user(T* dest, Userspace src) +{ + return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T)); +} + template [[nodiscard]] inline bool copy_to_user(Userspace dest, const T* src) { diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp index 70d30765dc9..14c8871869c 100644 --- a/Kernel/Syscalls/socket.cpp +++ b/Kernel/Syscalls/socket.cpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace Kernel { @@ -176,18 +177,24 @@ int Process::sys$shutdown(int sockfd, int how) return socket.shutdown(how); } -ssize_t Process::sys$sendto(Userspace user_params) +ssize_t Process::sys$sendmsg(int sockfd, Userspace user_msg, int flags) { REQUIRE_PROMISE(stdio); - Syscall::SC_sendto_params params; - if (!copy_from_user(¶ms, user_params)) + struct msghdr msg; + if (!copy_from_user(&msg, user_msg)) return -EFAULT; - int flags = params.flags; - Userspace addr((FlatPtr)params.addr); - socklen_t addr_length = params.addr_length; + if (msg.msg_iovlen != 1) + return -ENOTSUP; // FIXME: Support this :) + Vector iovs; + iovs.resize(msg.msg_iovlen); + if (!copy_n_from_user(iovs.data(), msg.msg_iov, msg.msg_iovlen)) + return -EFAULT; - auto description = file_description(params.sockfd); + Userspace user_addr((FlatPtr)msg.msg_name); + socklen_t addr_length = msg.msg_namelen; + + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -196,32 +203,36 @@ ssize_t Process::sys$sendto(Userspace user_par if (socket.is_shut_down_for_writing()) return -EPIPE; SmapDisabler disabler; - auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast((const u8*)params.data.data), params.data.size); + auto data_buffer = UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len); if (!data_buffer.has_value()) return -EFAULT; - auto result = socket.sendto(*description, data_buffer.value(), params.data.size, flags, addr, addr_length); + auto result = socket.sendto(*description, data_buffer.value(), iovs[0].iov_len, flags, user_addr, addr_length); if (result.is_error()) return result.error(); return result.value(); } -ssize_t Process::sys$recvfrom(Userspace user_params) +ssize_t Process::sys$recvmsg(int sockfd, Userspace user_msg, int flags) { REQUIRE_PROMISE(stdio); - Syscall::SC_recvfrom_params params; - if (!copy_from_user(¶ms, user_params)) + struct msghdr msg; + if (!copy_from_user(&msg, user_msg)) return -EFAULT; - int flags = params.flags; - Userspace user_addr((FlatPtr)params.addr); - Userspace user_addr_length((FlatPtr)params.addr_length); + if (msg.msg_iovlen != 1) + return -ENOTSUP; // FIXME: Support this :) + Vector iovs; + iovs.resize(msg.msg_iovlen); + if (!copy_n_from_user(iovs.data(), msg.msg_iov, msg.msg_iovlen)) + return -EFAULT; + + Userspace user_addr((FlatPtr)msg.msg_name); + Userspace user_addr_length(msg.msg_name ? (FlatPtr)&user_msg.unsafe_userspace_ptr()->msg_namelen : 0); SmapDisabler disabler; - if (!user_addr_length && user_addr) - return -EINVAL; - auto description = file_description(params.sockfd); + auto description = file_description(sockfd); if (!description) return -EBADF; if (!description->is_socket()) @@ -235,15 +246,26 @@ ssize_t Process::sys$recvfrom(Userspace user if (flags & MSG_DONTWAIT) description->set_blocking(false); - auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast((const u8*)params.buffer.data), params.buffer.size); + auto data_buffer = UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len); if (!data_buffer.has_value()) return -EFAULT; - auto result = socket.recvfrom(*description, data_buffer.value(), params.buffer.size, flags, user_addr, user_addr_length); + auto result = socket.recvfrom(*description, data_buffer.value(), iovs[0].iov_len, flags, user_addr, user_addr_length); if (flags & MSG_DONTWAIT) description->set_blocking(original_blocking); if (result.is_error()) return result.error(); + + int msg_flags = 0; + + if (result.value() > iovs[0].iov_len) { + ASSERT(socket.type() != SOCK_STREAM); + msg_flags |= MSG_TRUNC; + } + + if (!copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_flags, &msg_flags)) + return -EFAULT; + return result.value(); } diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index c2ea63aa60c..4df6475caf0 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -457,6 +457,7 @@ struct pollfd { #define SHUT_WR 2 #define SHUT_RDWR 3 +#define MSG_TRUNC 0x1 #define MSG_DONTWAIT 0x40 #define SOL_SOCKET 1 @@ -552,6 +553,16 @@ struct iovec { size_t iov_len; }; +struct msghdr { + void* msg_name; + socklen_t msg_namelen; + struct iovec* msg_iov; + int msg_iovlen; + void* msg_control; + socklen_t msg_controllen; + int msg_flags; +}; + struct sched_param { int sched_priority; }; diff --git a/Libraries/LibC/sys/socket.cpp b/Libraries/LibC/sys/socket.cpp index f6584bcb85e..55c754b32eb 100644 --- a/Libraries/LibC/sys/socket.cpp +++ b/Libraries/LibC/sys/socket.cpp @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include extern "C" { @@ -68,11 +70,17 @@ int shutdown(int sockfd, int how) __RETURN_WITH_ERRNO(rc, rc, -1); } +ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) +{ + int rc = syscall(SC_sendmsg, sockfd, msg, flags); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + ssize_t sendto(int sockfd, const void* data, size_t data_length, int flags, const struct sockaddr* addr, socklen_t addr_length) { - Syscall::SC_sendto_params params { sockfd, { data, data_length }, flags, addr, addr_length }; - int rc = syscall(SC_sendto, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); + iovec iov = { const_cast(data), data_length }; + msghdr msg = { const_cast(addr), addr_length, &iov, 1, nullptr, 0, 0 }; + return sendmsg(sockfd, &msg, flags); } ssize_t send(int sockfd, const void* data, size_t data_length, int flags) @@ -80,11 +88,28 @@ ssize_t send(int sockfd, const void* data, size_t data_length, int flags) return sendto(sockfd, data, data_length, flags, nullptr, 0); } +ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags) +{ + int rc = syscall(SC_recvmsg, sockfd, msg, flags); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + ssize_t recvfrom(int sockfd, void* buffer, size_t buffer_length, int flags, struct sockaddr* addr, socklen_t* addr_length) { - Syscall::SC_recvfrom_params params { sockfd, { buffer, buffer_length }, flags, addr, addr_length }; - int rc = syscall(SC_recvfrom, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); + if (!addr_length && addr) { + errno = EINVAL; + return -1; + } + + sockaddr_storage internal_addr; + iovec iov = { buffer, buffer_length }; + msghdr msg = { addr ? &internal_addr : nullptr, addr ? sizeof(internal_addr) : 0, &iov, 1, nullptr, 0, 0 }; + ssize_t rc = recvmsg(sockfd, &msg, flags); + if (rc >= 0 && addr) { + memcpy(addr, &internal_addr, min(*addr_length, msg.msg_namelen)); + *addr_length = msg.msg_namelen; + } + return rc; } ssize_t recv(int sockfd, void* buffer, size_t buffer_length, int flags) diff --git a/Libraries/LibC/sys/socket.h b/Libraries/LibC/sys/socket.h index f14e1d70059..898269fb8e9 100644 --- a/Libraries/LibC/sys/socket.h +++ b/Libraries/LibC/sys/socket.h @@ -61,10 +61,21 @@ __BEGIN_DECLS #define IPPROTO_TCP 6 #define IPPROTO_UDP 17 +#define MSG_TRUNC 0x1 #define MSG_DONTWAIT 0x40 typedef uint16_t sa_family_t; +struct msghdr { + void* msg_name; + socklen_t msg_namelen; + struct iovec* msg_iov; + int msg_iovlen; + void* msg_control; + socklen_t msg_controllen; + int msg_flags; +}; + struct sockaddr { sa_family_t sa_family; char sa_data[14]; @@ -102,8 +113,10 @@ int accept(int sockfd, struct sockaddr*, socklen_t*); int connect(int sockfd, const struct sockaddr*, socklen_t); int shutdown(int sockfd, int how); ssize_t send(int sockfd, const void*, size_t, int flags); +ssize_t sendmsg(int sockfd, const struct msghdr*, int flags); ssize_t sendto(int sockfd, const void*, size_t, int flags, const struct sockaddr*, socklen_t); ssize_t recv(int sockfd, void*, size_t, int flags); +ssize_t recvmsg(int sockfd, struct msghdr*, int flags); ssize_t recvfrom(int sockfd, void*, size_t, int flags, struct sockaddr*, socklen_t*); int getsockopt(int sockfd, int level, int option, void*, socklen_t*); int setsockopt(int sockfd, int level, int option, const void*, socklen_t);