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.
This commit is contained in:
Nico Weber 2020-09-16 11:45:00 -04:00 committed by Andreas Kling
parent 19f2203b53
commit b36a2d6686
Notes: sideshowbarker 2024-07-19 02:21:56 +09:00
9 changed files with 158 additions and 79 deletions

View File

@ -43,6 +43,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <termios.h>
#include <unistd.h>
@ -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(&params, 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(&params, 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)

View File

@ -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);

View File

@ -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<void, size_t> data;
int flags;
const sockaddr* addr;
socklen_t addr_length;
};
struct SC_recvfrom_params {
int sockfd;
MutableBufferArgument<void, size_t> buffer;
int flags;
sockaddr* addr;
socklen_t* addr_length;
};
struct SC_getsockopt_params {
int sockfd;
int level;

View File

@ -290,8 +290,8 @@ public:
int sys$accept(int sockfd, Userspace<sockaddr*>, Userspace<socklen_t*>);
int sys$connect(int sockfd, Userspace<const sockaddr*>, socklen_t);
int sys$shutdown(int sockfd, int how);
ssize_t sys$sendto(Userspace<const Syscall::SC_sendto_params*>);
ssize_t sys$recvfrom(Userspace<const Syscall::SC_recvfrom_params*>);
ssize_t sys$sendmsg(int sockfd, Userspace<const struct msghdr*>, int flags);
ssize_t sys$recvmsg(int sockfd, Userspace<struct msghdr*>, int flags);
int sys$getsockopt(Userspace<const Syscall::SC_getsockopt_params*>);
int sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*>);
int sys$getsockname(Userspace<const Syscall::SC_getsockname_params*>);

View File

@ -76,6 +76,12 @@ template<typename T>
return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T));
}
template<typename T>
[[nodiscard]] inline bool copy_from_user(T* dest, Userspace<T*> src)
{
return copy_from_user(dest, src.unsafe_userspace_ptr(), sizeof(T));
}
template<typename T>
[[nodiscard]] inline bool copy_to_user(Userspace<T*> dest, const T* src)
{

View File

@ -28,6 +28,7 @@
#include <Kernel/Net/IPv4Socket.h>
#include <Kernel/Net/LocalSocket.h>
#include <Kernel/Process.h>
#include <Kernel/UnixTypes.h>
namespace Kernel {
@ -176,18 +177,24 @@ int Process::sys$shutdown(int sockfd, int how)
return socket.shutdown(how);
}
ssize_t Process::sys$sendto(Userspace<const Syscall::SC_sendto_params*> user_params)
ssize_t Process::sys$sendmsg(int sockfd, Userspace<const struct msghdr*> user_msg, int flags)
{
REQUIRE_PROMISE(stdio);
Syscall::SC_sendto_params params;
if (!copy_from_user(&params, user_params))
struct msghdr msg;
if (!copy_from_user(&msg, user_msg))
return -EFAULT;
int flags = params.flags;
Userspace<const sockaddr*> addr((FlatPtr)params.addr);
socklen_t addr_length = params.addr_length;
if (msg.msg_iovlen != 1)
return -ENOTSUP; // FIXME: Support this :)
Vector<iovec, 1> 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<const sockaddr*> 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<const Syscall::SC_sendto_params*> user_par
if (socket.is_shut_down_for_writing())
return -EPIPE;
SmapDisabler disabler;
auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>((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<const Syscall::SC_recvfrom_params*> user_params)
ssize_t Process::sys$recvmsg(int sockfd, Userspace<struct msghdr*> user_msg, int flags)
{
REQUIRE_PROMISE(stdio);
Syscall::SC_recvfrom_params params;
if (!copy_from_user(&params, user_params))
struct msghdr msg;
if (!copy_from_user(&msg, user_msg))
return -EFAULT;
int flags = params.flags;
Userspace<sockaddr*> user_addr((FlatPtr)params.addr);
Userspace<socklen_t*> user_addr_length((FlatPtr)params.addr_length);
if (msg.msg_iovlen != 1)
return -ENOTSUP; // FIXME: Support this :)
Vector<iovec, 1> iovs;
iovs.resize(msg.msg_iovlen);
if (!copy_n_from_user(iovs.data(), msg.msg_iov, msg.msg_iovlen))
return -EFAULT;
Userspace<sockaddr*> user_addr((FlatPtr)msg.msg_name);
Userspace<socklen_t*> 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<const Syscall::SC_recvfrom_params*> user
if (flags & MSG_DONTWAIT)
description->set_blocking(false);
auto data_buffer = UserOrKernelBuffer::for_user_buffer(const_cast<u8*>((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();
}

View File

@ -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;
};

View File

@ -28,7 +28,9 @@
#include <Kernel/API/Syscall.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/uio.h>
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, &params);
__RETURN_WITH_ERRNO(rc, rc, -1);
iovec iov = { const_cast<void*>(data), data_length };
msghdr msg = { const_cast<struct sockaddr*>(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, &params);
__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)

View File

@ -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);