Просмотр исходного кода

Kernel+LibC: Implement the socketpair() syscall

Gunnar Beutner 4 лет назад
Родитель
Сommit
aa792062cb

+ 8 - 0
Kernel/API/Syscall.h

@@ -120,6 +120,7 @@ namespace Kernel {
     S(beep)                   \
     S(getsockname)            \
     S(getpeername)            \
+    S(socketpair)             \
     S(sched_setparam)         \
     S(sched_getparam)         \
     S(fchown)                 \
@@ -293,6 +294,13 @@ struct SC_getpeername_params {
     socklen_t* addrlen;
 };
 
+struct SC_socketpair_params {
+    int domain;
+    int type;
+    int protocol;
+    int* sv;
+};
+
 struct SC_futex_params {
     u32* userspace_address;
     int futex_op;

+ 25 - 0
Kernel/Net/LocalSocket.cpp

@@ -36,6 +36,31 @@ KResultOr<NonnullRefPtr<Socket>> LocalSocket::create(int type)
     return adopt_ref(*new LocalSocket(type));
 }
 
+KResultOr<SocketPair> LocalSocket::create_connected_pair(int type)
+{
+    auto socket = adopt_ref(*new LocalSocket(type));
+
+    auto description1_result = FileDescription::create(*socket);
+    if (description1_result.is_error())
+        return description1_result.error();
+
+    socket->m_address.sun_family = AF_LOCAL;
+    memcpy(socket->m_address.sun_path, "[socketpair]", 13);
+
+    auto& process = *Process::current();
+    socket->m_acceptor = { process.pid().value(), process.uid(), process.gid() };
+
+    socket->set_connected(true);
+    socket->set_connect_side_role(Role::Connected);
+    socket->m_role = Role::Accepted;
+
+    auto description2_result = FileDescription::create(*socket);
+    if (description2_result.is_error())
+        return description2_result.error();
+
+    return SocketPair { description1_result.release_value(), description2_result.release_value() };
+}
+
 LocalSocket::LocalSocket(int type)
     : Socket(AF_LOCAL, type, 0)
 {

+ 6 - 0
Kernel/Net/LocalSocket.h

@@ -14,12 +14,18 @@ namespace Kernel {
 
 class FileDescription;
 
+struct SocketPair {
+    NonnullRefPtr<FileDescription> description1;
+    NonnullRefPtr<FileDescription> description2;
+};
+
 class LocalSocket final : public Socket
     , public InlineLinkedListNode<LocalSocket> {
     friend class InlineLinkedListNode<LocalSocket>;
 
 public:
     static KResultOr<NonnullRefPtr<Socket>> create(int type);
+    static KResultOr<SocketPair> create_connected_pair(int type);
     virtual ~LocalSocket() override;
 
     KResult sendfd(const FileDescription& socket_description, FileDescription& passing_description);

+ 3 - 0
Kernel/Process.h

@@ -355,6 +355,7 @@ public:
     KResultOr<int> sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*>);
     KResultOr<int> sys$getsockname(Userspace<const Syscall::SC_getsockname_params*>);
     KResultOr<int> sys$getpeername(Userspace<const Syscall::SC_getpeername_params*>);
+    KResultOr<int> sys$socketpair(Userspace<const Syscall::SC_socketpair_params*>);
     KResultOr<int> sys$sched_setparam(pid_t pid, Userspace<const struct sched_param*>);
     KResultOr<int> sys$sched_getparam(pid_t pid, Userspace<struct sched_param*>);
     KResultOr<int> sys$create_thread(void* (*)(void*), Userspace<const Syscall::SC_create_thread_params*>);
@@ -529,6 +530,8 @@ private:
 
     void clear_futex_queues_on_exec();
 
+    void setup_socket_fd(int fd, NonnullRefPtr<FileDescription> description, int type);
+
     inline PerformanceEventBuffer* current_perf_events_buffer()
     {
         return g_profiling_all_threads ? g_global_perf_events : m_perf_event_buffer.ptr();

+ 50 - 8
Kernel/Syscalls/socket.cpp

@@ -20,6 +20,18 @@ namespace Kernel {
             REQUIRE_PROMISE(unix);                \
     } while (0)
 
+void Process::setup_socket_fd(int fd, NonnullRefPtr<FileDescription> description, int type)
+{
+    description->set_readable(true);
+    description->set_writable(true);
+    unsigned flags = 0;
+    if (type & SOCK_CLOEXEC)
+        flags |= FD_CLOEXEC;
+    if (type & SOCK_NONBLOCK)
+        description->set_blocking(false);
+    m_fds[fd].set(*description, flags);
+}
+
 KResultOr<int> Process::sys$socket(int domain, int type, int protocol)
 {
     REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(domain);
@@ -35,14 +47,7 @@ KResultOr<int> Process::sys$socket(int domain, int type, int protocol)
     auto description_result = FileDescription::create(*result.value());
     if (description_result.is_error())
         return description_result.error();
-    description_result.value()->set_readable(true);
-    description_result.value()->set_writable(true);
-    unsigned flags = 0;
-    if (type & SOCK_CLOEXEC)
-        flags |= FD_CLOEXEC;
-    if (type & SOCK_NONBLOCK)
-        description_result.value()->set_blocking(false);
-    m_fds[fd].set(description_result.release_value(), flags);
+    setup_socket_fd(fd, description_result.value(), type);
     return fd;
 }
 
@@ -362,4 +367,41 @@ KResultOr<int> Process::sys$setsockopt(Userspace<const Syscall::SC_setsockopt_pa
     return socket.setsockopt(params.level, params.option, user_value, params.value_size);
 }
 
+KResultOr<int> Process::sys$socketpair(Userspace<const Syscall::SC_socketpair_params*> user_params)
+{
+    Syscall::SC_socketpair_params params;
+    if (!copy_from_user(&params, user_params))
+        return EFAULT;
+
+    if (params.domain != AF_LOCAL)
+        return EINVAL;
+
+    if (params.protocol != 0 && params.protocol != PF_LOCAL)
+        return EINVAL;
+
+    auto result = LocalSocket::create_connected_pair(params.type & SOCK_TYPE_MASK);
+    if (result.is_error())
+        return result.error();
+    auto pair = result.value();
+
+    int fds[2];
+    fds[0] = alloc_fd();
+    if (fds[0] < 0)
+        return ENFILE;
+    setup_socket_fd(fds[0], pair.description1, params.type);
+
+    fds[1] = alloc_fd();
+    if (fds[1] < 0) {
+        // FIXME: This leaks fds[0]
+        return ENFILE;
+    }
+    setup_socket_fd(fds[1], pair.description2, params.type);
+
+    if (!copy_to_user(params.sv, fds, sizeof(fds))) {
+        // FIXME: This leaks both file descriptors
+        return EFAULT;
+    }
+
+    return KSuccess;
+}
 }

+ 0 - 16
Ports/libassuan/patches/socketpair.patch

@@ -1,16 +0,0 @@
-diff -Naur libassuan-2.5.5/src/system-posix.c libassuan-2.5.5.serenity/src/system-posix.c
---- libassuan-2.5.5/src/system-posix.c	2021-04-14 02:40:14.020341296 +0200
-+++ libassuan-2.5.5.serenity/src/system-posix.c	2021-04-14 02:39:56.823341349 +0200
-@@ -412,7 +412,12 @@
- __assuan_socketpair (assuan_context_t ctx, int namespace, int style,
- 		     int protocol, assuan_fd_t filedes[2])
- {
-+#ifndef __serenity__
-   return socketpair (namespace, style, protocol, filedes);
-+#else
-+  errno = ENOTSUP;
-+  return -1;
-+#endif
- }
- 
- 

+ 0 - 36
Ports/openssh/patches/missing_functionality.patch

@@ -150,21 +150,6 @@ index 554ceb0b..67464ef2 100644
  #include <netinet/ip.h>
  #include <netinet/tcp.h>
  #include <arpa/inet.h>
-diff --git a/monitor.c b/monitor.c
-index b6e855d5..bde8f383 100644
---- a/monitor.c
-+++ b/monitor.c
-@@ -1752,8 +1752,10 @@ monitor_openfds(struct monitor *mon, int do_logfds)
- 	int on = 1;
- #endif
- 
-+#ifndef __serenity__
- 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
- 		fatal("%s: socketpair: %s", __func__, strerror(errno));
-+#endif
- #ifdef SO_ZEROIZE
- 	if (setsockopt(pair[0], SOL_SOCKET, SO_ZEROIZE, &on, sizeof(on)) == -1)
- 		error("setsockopt SO_ZEROIZE(0): %.100s", strerror(errno));
 diff --git a/openbsd-compat/bsd-misc.c b/openbsd-compat/bsd-misc.c
 index 059b6d3b..2a248c81 100644
 --- a/openbsd-compat/bsd-misc.c
@@ -588,27 +573,6 @@ index af08be41..9e748a23 100644
  	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
  	    options.user_hostfiles, options.num_user_hostfiles,
  	    options.system_hostfiles, options.num_system_hostfiles);
-diff --git a/sshd.c b/sshd.c
-index 6f8f11a3..1ecf3e32 100644
---- a/sshd.c
-+++ b/sshd.c
-@@ -1231,6 +1231,8 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
- 				continue;
- 			}
- 
-+// FIXME: socketpair is seemingly required for SSHD to work, but doesn't current exist.
-+#ifndef __serenity__
- 			if (rexec_flag && socketpair(AF_UNIX,
- 			    SOCK_STREAM, 0, config_s) == -1) {
- 				error("reexec socketpair: %s",
-@@ -1240,6 +1242,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
- 				close(startup_p[1]);
- 				continue;
- 			}
-+#endif
- 
- 			for (j = 0; j < options.max_startups; j++)
- 				if (startup_pipes[j] == -1) {
 diff --git a/sshkey.c b/sshkey.c
 index 1571e3d9..2b5c611c 100644
 --- a/sshkey.c

+ 0 - 16
Ports/openssh/patches/sftp_pipes.patch

@@ -1,16 +0,0 @@
-e5a0b5cc530260b1ee94105e8c989ba21856b4a2 Use pipes instead of socketpair in SFTP
-diff --git a/sftp.c b/sftp.c
-index 2799e4a1..9ce7055a 100644
---- a/sftp.c
-+++ b/sftp.c
-@@ -2296,6 +2296,10 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
- 	return (err >= 0 ? 0 : -1);
- }
- 
-+#ifdef __serenity__
-+#define USE_PIPES 1
-+#endif
-+
- static void
- connect_to_server(char *path, char **args, int *in, int *out)
- {

+ 0 - 15
Ports/stress-ng/patches/0003-missing-networking-functionality.patch

@@ -79,18 +79,3 @@ diff -ur a/stress-resources.c b/stress-resources.c
  	static const int types[] = { SOCK_STREAM, SOCK_DGRAM };
  	static stress_info_t info[MAX_LOOPS];
  #if defined(O_NOATIME)
-@@ -309,11 +309,13 @@
- 		if (!keep_stressing_flag())
- 			break;
- 
-+#if 0
- 		if (socketpair(AF_UNIX, SOCK_STREAM, 0,
- 			info[i].fd_socketpair) < 0) {
- 			info[i].fd_socketpair[0] = -1;
- 			info[i].fd_socketpair[1] = -1;
- 		}
-+#endif
- 
- #if defined(HAVE_USERFAULTFD)
- 		info[i].fd_uf = shim_userfaultfd(0);
-d

+ 7 - 0
Userland/Libraries/LibC/sys/socket.cpp

@@ -125,6 +125,13 @@ int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
     __RETURN_WITH_ERRNO(rc, rc, -1);
 }
 
+int socketpair(int domain, int type, int protocol, int sv[2])
+{
+    Syscall::SC_socketpair_params params { domain, type, protocol, sv };
+    int rc = syscall(SC_socketpair, &params);
+    __RETURN_WITH_ERRNO(rc, rc, -1);
+}
+
 int sendfd(int sockfd, int fd)
 {
     int rc = syscall(SC_sendfd, sockfd, fd);

+ 1 - 0
Userland/Libraries/LibC/sys/socket.h

@@ -138,6 +138,7 @@ int getsockopt(int sockfd, int level, int option, void*, socklen_t*);
 int setsockopt(int sockfd, int level, int option, const void*, socklen_t);
 int getsockname(int sockfd, struct sockaddr*, socklen_t*);
 int getpeername(int sockfd, struct sockaddr*, socklen_t*);
+int socketpair(int domain, int type, int protocol, int sv[2]);
 int sendfd(int sockfd, int fd);
 int recvfd(int sockfd, int options);