Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/ByteBuffer.h>
8#include <Kernel/FileSystem/OpenFileDescription.h>
9#include <Kernel/Net/LocalSocket.h>
10#include <Kernel/Process.h>
11#include <Kernel/UnixTypes.h>
12
13namespace Kernel {
14
15#define REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(domain) \
16 do { \
17 if (domain == AF_INET) \
18 TRY(require_promise(Pledge::inet)); \
19 else if (domain == AF_LOCAL) \
20 TRY(require_promise(Pledge::unix)); \
21 } while (0)
22
23static void setup_socket_fd(Process::OpenFileDescriptions& fds, int fd, NonnullRefPtr<OpenFileDescription> description, int type)
24{
25 description->set_readable(true);
26 description->set_writable(true);
27 unsigned flags = 0;
28 if (type & SOCK_CLOEXEC)
29 flags |= FD_CLOEXEC;
30 if (type & SOCK_NONBLOCK)
31 description->set_blocking(false);
32 fds[fd].set(*description, flags);
33}
34
35ErrorOr<FlatPtr> Process::sys$socket(int domain, int type, int protocol)
36{
37 VERIFY_NO_PROCESS_BIG_LOCK(this);
38 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(domain);
39
40 auto credentials = this->credentials();
41 if ((type & SOCK_TYPE_MASK) == SOCK_RAW && !credentials->is_superuser())
42 return EACCES;
43
44 return m_fds.with_exclusive([&](auto& fds) -> ErrorOr<FlatPtr> {
45 auto fd_allocation = TRY(fds.allocate());
46 auto socket = TRY(Socket::create(domain, type, protocol));
47 auto description = TRY(OpenFileDescription::try_create(socket));
48 setup_socket_fd(fds, fd_allocation.fd, move(description), type);
49 return fd_allocation.fd;
50 });
51}
52
53ErrorOr<FlatPtr> Process::sys$bind(int sockfd, Userspace<sockaddr const*> address, socklen_t address_length)
54{
55 VERIFY_NO_PROCESS_BIG_LOCK(this);
56 auto description = TRY(open_file_description(sockfd));
57 if (!description->is_socket())
58 return ENOTSOCK;
59 auto& socket = *description->socket();
60 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
61 TRY(socket.bind(credentials(), address, address_length));
62 return 0;
63}
64
65ErrorOr<FlatPtr> Process::sys$listen(int sockfd, int backlog)
66{
67 VERIFY_NO_PROCESS_BIG_LOCK(this);
68 if (backlog < 0)
69 return EINVAL;
70 auto description = TRY(open_file_description(sockfd));
71 if (!description->is_socket())
72 return ENOTSOCK;
73 auto& socket = *description->socket();
74 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
75 if (socket.is_connected())
76 return EINVAL;
77 TRY(socket.listen(backlog));
78 return 0;
79}
80
81ErrorOr<FlatPtr> Process::sys$accept4(Userspace<Syscall::SC_accept4_params const*> user_params)
82{
83 VERIFY_NO_PROCESS_BIG_LOCK(this);
84 TRY(require_promise(Pledge::accept));
85 auto params = TRY(copy_typed_from_user(user_params));
86
87 int accepting_socket_fd = params.sockfd;
88 Userspace<sockaddr*> user_address((FlatPtr)params.addr);
89 Userspace<socklen_t*> user_address_size((FlatPtr)params.addrlen);
90 int flags = params.flags;
91
92 socklen_t address_size = 0;
93 if (user_address)
94 TRY(copy_from_user(&address_size, static_ptr_cast<socklen_t const*>(user_address_size)));
95
96 ScopedDescriptionAllocation fd_allocation;
97 RefPtr<OpenFileDescription> accepting_socket_description;
98
99 TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
100 fd_allocation = TRY(fds.allocate());
101 accepting_socket_description = TRY(fds.open_file_description(accepting_socket_fd));
102 return {};
103 }));
104 if (!accepting_socket_description->is_socket())
105 return ENOTSOCK;
106 auto& socket = *accepting_socket_description->socket();
107
108 LockRefPtr<Socket> accepted_socket;
109 for (;;) {
110 accepted_socket = socket.accept();
111 if (accepted_socket)
112 break;
113 if (!accepting_socket_description->is_blocking())
114 return EAGAIN;
115 auto unblock_flags = Thread::FileBlocker::BlockFlags::None;
116 if (Thread::current()->block<Thread::AcceptBlocker>({}, *accepting_socket_description, unblock_flags).was_interrupted())
117 return EINTR;
118 }
119
120 if (user_address) {
121 sockaddr_un address_buffer {};
122 address_size = min(sizeof(sockaddr_un), static_cast<size_t>(address_size));
123 accepted_socket->get_peer_address((sockaddr*)&address_buffer, &address_size);
124 TRY(copy_to_user(user_address, &address_buffer, address_size));
125 TRY(copy_to_user(user_address_size, &address_size));
126 }
127
128 auto accepted_socket_description = TRY(OpenFileDescription::try_create(*accepted_socket));
129
130 accepted_socket_description->set_readable(true);
131 accepted_socket_description->set_writable(true);
132 if (flags & SOCK_NONBLOCK)
133 accepted_socket_description->set_blocking(false);
134 int fd_flags = 0;
135 if (flags & SOCK_CLOEXEC)
136 fd_flags |= FD_CLOEXEC;
137
138 TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
139 fds[fd_allocation.fd].set(move(accepted_socket_description), fd_flags);
140 return {};
141 }));
142
143 // NOTE: Moving this state to Completed is what causes connect() to unblock on the client side.
144 accepted_socket->set_setup_state(Socket::SetupState::Completed);
145 return fd_allocation.fd;
146}
147
148ErrorOr<FlatPtr> Process::sys$connect(int sockfd, Userspace<sockaddr const*> user_address, socklen_t user_address_size)
149{
150 VERIFY_NO_PROCESS_BIG_LOCK(this);
151
152 auto description = TRY(open_file_description(sockfd));
153 if (!description->is_socket())
154 return ENOTSOCK;
155 auto& socket = *description->socket();
156 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
157 TRY(socket.connect(credentials(), *description, user_address, user_address_size));
158 return 0;
159}
160
161ErrorOr<FlatPtr> Process::sys$shutdown(int sockfd, int how)
162{
163 VERIFY_NO_PROCESS_BIG_LOCK(this);
164 TRY(require_promise(Pledge::stdio));
165 if (how != SHUT_RD && how != SHUT_WR && how != SHUT_RDWR)
166 return EINVAL;
167 auto description = TRY(open_file_description(sockfd));
168 if (!description->is_socket())
169 return ENOTSOCK;
170 auto& socket = *description->socket();
171 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
172 TRY(socket.shutdown(how));
173 return 0;
174}
175
176ErrorOr<FlatPtr> Process::sys$sendmsg(int sockfd, Userspace<const struct msghdr*> user_msg, int flags)
177{
178 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
179 TRY(require_promise(Pledge::stdio));
180 auto msg = TRY(copy_typed_from_user(user_msg));
181
182 if (msg.msg_iovlen != 1)
183 return ENOTSUP; // FIXME: Support this :)
184 Vector<iovec, 1> iovs;
185 TRY(iovs.try_resize(msg.msg_iovlen));
186 TRY(copy_n_from_user(iovs.data(), msg.msg_iov, msg.msg_iovlen));
187 if (iovs[0].iov_len > NumericLimits<ssize_t>::max())
188 return EINVAL;
189
190 Userspace<sockaddr const*> user_addr((FlatPtr)msg.msg_name);
191 socklen_t addr_length = msg.msg_namelen;
192
193 auto description = TRY(open_file_description(sockfd));
194 if (!description->is_socket())
195 return ENOTSOCK;
196
197 auto& socket = *description->socket();
198 if (socket.is_shut_down_for_writing()) {
199 if ((flags & MSG_NOSIGNAL) == 0)
200 Thread::current()->send_signal(SIGPIPE, &Process::current());
201 return EPIPE;
202 }
203
204 if (msg.msg_controllen > 0) {
205 // Handle command messages.
206 auto cmsg_buffer = TRY(ByteBuffer::create_uninitialized(msg.msg_controllen));
207 TRY(copy_from_user(cmsg_buffer.data(), msg.msg_control, msg.msg_controllen));
208 msg.msg_control = cmsg_buffer.data();
209 for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
210 if (socket.is_local() && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
211 auto& local_socket = static_cast<LocalSocket&>(socket);
212 int* fds = (int*)CMSG_DATA(cmsg);
213 size_t nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) / sizeof(int);
214 for (size_t i = 0; i < nfds; ++i) {
215 TRY(local_socket.sendfd(*description, TRY(open_file_description(fds[i]))));
216 }
217 }
218 }
219 }
220
221 auto data_buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len));
222
223 while (true) {
224 while (!description->can_write()) {
225 if (!description->is_blocking()) {
226 return EAGAIN;
227 }
228
229 auto unblock_flags = Thread::FileBlocker::BlockFlags::None;
230 if (Thread::current()->block<Thread::WriteBlocker>({}, *description, unblock_flags).was_interrupted()) {
231 return EINTR;
232 }
233 // TODO: handle exceptions in unblock_flags
234 }
235
236 auto bytes_sent_or_error = socket.sendto(*description, data_buffer, iovs[0].iov_len, flags, user_addr, addr_length);
237 if (bytes_sent_or_error.is_error()) {
238 if ((flags & MSG_NOSIGNAL) == 0 && bytes_sent_or_error.error().code() == EPIPE)
239 Thread::current()->send_signal(SIGPIPE, &Process::current());
240 return bytes_sent_or_error.release_error();
241 }
242
243 auto bytes_sent = bytes_sent_or_error.release_value();
244 if (bytes_sent > 0)
245 return bytes_sent;
246 }
247}
248
249ErrorOr<FlatPtr> Process::sys$recvmsg(int sockfd, Userspace<struct msghdr*> user_msg, int flags)
250{
251 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
252 TRY(require_promise(Pledge::stdio));
253
254 struct msghdr msg;
255 TRY(copy_from_user(&msg, user_msg));
256
257 if (msg.msg_iovlen != 1)
258 return ENOTSUP; // FIXME: Support this :)
259 Vector<iovec, 1> iovs;
260 TRY(iovs.try_resize(msg.msg_iovlen));
261 TRY(copy_n_from_user(iovs.data(), msg.msg_iov, msg.msg_iovlen));
262
263 Userspace<sockaddr*> user_addr((FlatPtr)msg.msg_name);
264 Userspace<socklen_t*> user_addr_length(msg.msg_name ? (FlatPtr)&user_msg.unsafe_userspace_ptr()->msg_namelen : 0);
265
266 auto description = TRY(open_file_description(sockfd));
267 if (!description->is_socket())
268 return ENOTSOCK;
269 auto& socket = *description->socket();
270
271 if (socket.is_shut_down_for_reading())
272 return 0;
273
274 auto data_buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len));
275 Time timestamp {};
276 bool blocking = (flags & MSG_DONTWAIT) ? false : description->is_blocking();
277 auto result = socket.recvfrom(*description, data_buffer, iovs[0].iov_len, flags, user_addr, user_addr_length, timestamp, blocking);
278
279 if (result.is_error())
280 return result.release_error();
281
282 int msg_flags = 0;
283
284 if (result.value() > iovs[0].iov_len) {
285 VERIFY(socket.type() != SOCK_STREAM);
286 msg_flags |= MSG_TRUNC;
287 }
288
289 socklen_t current_cmsg_len = 0;
290 auto try_add_cmsg = [&](int level, int type, void const* data, socklen_t len) -> ErrorOr<bool> {
291 if (current_cmsg_len + len > msg.msg_controllen) {
292 msg_flags |= MSG_CTRUNC;
293 return false;
294 }
295
296 cmsghdr cmsg = { (socklen_t)CMSG_LEN(len), level, type };
297 cmsghdr* target = (cmsghdr*)(((char*)msg.msg_control) + current_cmsg_len);
298 TRY(copy_to_user(target, &cmsg));
299 TRY(copy_to_user(CMSG_DATA(target), data, len));
300 current_cmsg_len += CMSG_ALIGN(cmsg.cmsg_len);
301 return true;
302 };
303
304 if (socket.wants_timestamp()) {
305 timeval time = timestamp.to_timeval();
306 TRY(try_add_cmsg(SOL_SOCKET, SCM_TIMESTAMP, &time, sizeof(time)));
307 }
308
309 int space_for_fds = (msg.msg_controllen - current_cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
310 if (space_for_fds > 0 && socket.is_local()) {
311 auto& local_socket = static_cast<LocalSocket&>(socket);
312 auto descriptions = TRY(local_socket.recvfds(description, space_for_fds));
313 Vector<int> fdnums;
314 for (auto& description : descriptions) {
315 auto fd_allocation = TRY(m_fds.with_exclusive([](auto& fds) { return fds.allocate(); }));
316 m_fds.with_exclusive([&](auto& fds) { fds[fd_allocation.fd].set(*description, 0); });
317 fdnums.append(fd_allocation.fd);
318 }
319 TRY(try_add_cmsg(SOL_SOCKET, SCM_RIGHTS, fdnums.data(), fdnums.size() * sizeof(int)));
320 }
321
322 TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_controllen, ¤t_cmsg_len));
323
324 TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_flags, &msg_flags));
325 return result.value();
326}
327
328template<bool sockname, typename Params>
329ErrorOr<void> Process::get_sock_or_peer_name(Params const& params)
330{
331 socklen_t addrlen_value;
332 TRY(copy_from_user(&addrlen_value, params.addrlen, sizeof(socklen_t)));
333
334 if (addrlen_value <= 0)
335 return EINVAL;
336
337 auto description = TRY(open_file_description(params.sockfd));
338 if (!description->is_socket())
339 return ENOTSOCK;
340
341 auto& socket = *description->socket();
342 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
343
344 sockaddr_un address_buffer {};
345 addrlen_value = min(sizeof(sockaddr_un), static_cast<size_t>(addrlen_value));
346 if constexpr (sockname)
347 socket.get_local_address((sockaddr*)&address_buffer, &addrlen_value);
348 else
349 socket.get_peer_address((sockaddr*)&address_buffer, &addrlen_value);
350 TRY(copy_to_user(params.addr, &address_buffer, addrlen_value));
351 return copy_to_user(params.addrlen, &addrlen_value);
352}
353
354ErrorOr<FlatPtr> Process::sys$getsockname(Userspace<Syscall::SC_getsockname_params const*> user_params)
355{
356 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
357 auto params = TRY(copy_typed_from_user(user_params));
358 TRY(get_sock_or_peer_name<true>(params));
359 return 0;
360}
361
362ErrorOr<FlatPtr> Process::sys$getpeername(Userspace<Syscall::SC_getpeername_params const*> user_params)
363{
364 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
365 auto params = TRY(copy_typed_from_user(user_params));
366 TRY(get_sock_or_peer_name<false>(params));
367 return 0;
368}
369
370ErrorOr<FlatPtr> Process::sys$getsockopt(Userspace<Syscall::SC_getsockopt_params const*> user_params)
371{
372 VERIFY_NO_PROCESS_BIG_LOCK(this);
373 auto params = TRY(copy_typed_from_user(user_params));
374
375 int sockfd = params.sockfd;
376 int level = params.level;
377 int option = params.option;
378 Userspace<void*> user_value((FlatPtr)params.value);
379 Userspace<socklen_t*> user_value_size((FlatPtr)params.value_size);
380
381 socklen_t value_size;
382 TRY(copy_from_user(&value_size, params.value_size, sizeof(socklen_t)));
383
384 auto description = TRY(open_file_description(sockfd));
385 if (!description->is_socket())
386 return ENOTSOCK;
387 auto& socket = *description->socket();
388 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
389 TRY(socket.getsockopt(*description, level, option, user_value, user_value_size));
390 return 0;
391}
392
393ErrorOr<FlatPtr> Process::sys$setsockopt(Userspace<Syscall::SC_setsockopt_params const*> user_params)
394{
395 VERIFY_NO_PROCESS_BIG_LOCK(this);
396 auto params = TRY(copy_typed_from_user(user_params));
397
398 Userspace<void const*> user_value((FlatPtr)params.value);
399 auto description = TRY(open_file_description(params.sockfd));
400 if (!description->is_socket())
401 return ENOTSOCK;
402 auto& socket = *description->socket();
403 REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
404 TRY(socket.setsockopt(params.level, params.option, user_value, params.value_size));
405 return 0;
406}
407
408ErrorOr<FlatPtr> Process::sys$socketpair(Userspace<Syscall::SC_socketpair_params const*> user_params)
409{
410 VERIFY_NO_PROCESS_BIG_LOCK(this);
411 auto params = TRY(copy_typed_from_user(user_params));
412
413 if (params.domain != AF_LOCAL)
414 return EINVAL;
415
416 if (params.protocol != 0 && params.protocol != PF_LOCAL)
417 return EINVAL;
418
419 auto pair = TRY(LocalSocket::try_create_connected_pair(params.type & SOCK_TYPE_MASK));
420
421 return m_fds.with_exclusive([&](auto& fds) -> ErrorOr<FlatPtr> {
422 auto fd_allocation0 = TRY(fds.allocate());
423 auto fd_allocation1 = TRY(fds.allocate());
424
425 int allocated_fds[2];
426 allocated_fds[0] = fd_allocation0.fd;
427 allocated_fds[1] = fd_allocation1.fd;
428 setup_socket_fd(fds, allocated_fds[0], pair.description0, params.type);
429 setup_socket_fd(fds, allocated_fds[1], pair.description1, params.type);
430
431 if (copy_to_user(params.sv, allocated_fds, sizeof(allocated_fds)).is_error()) {
432 // Avoid leaking both file descriptors on error.
433 fds[allocated_fds[0]] = {};
434 fds[allocated_fds[1]] = {};
435 return EFAULT;
436 }
437 return 0;
438 });
439}
440
441}