mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:52:45 +00:00 
			
		
		
		
	Kernel: Support sending filedescriptors with sendmsg(2) and SCM_RIGHTS
This is necessary to support the wayland protocol. I also moved the CMSG_* macros to the kernel API since they are used in both kernel and userspace. this does not break ntpquery/SCM_TIMESTAMP.
This commit is contained in:
		
							parent
							
								
									ae5d7f542c
								
							
						
					
					
						commit
						f20902deb3
					
				
					 5 changed files with 97 additions and 37 deletions
				
			
		|  | @ -81,6 +81,32 @@ struct msghdr { | ||||||
|     int msg_flags; |     int msg_flags; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // These three are non-POSIX, but common:
 | ||||||
|  | #define CMSG_ALIGN(x) (((x) + sizeof(void*) - 1) & ~(sizeof(void*) - 1)) | ||||||
|  | #define CMSG_SPACE(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(x)) | ||||||
|  | #define CMSG_LEN(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (x)) | ||||||
|  | 
 | ||||||
|  | static inline struct cmsghdr* CMSG_FIRSTHDR(struct msghdr* msg) | ||||||
|  | { | ||||||
|  |     if (msg->msg_controllen < sizeof(struct cmsghdr)) | ||||||
|  |         return (struct cmsghdr*)0; | ||||||
|  |     return (struct cmsghdr*)msg->msg_control; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline struct cmsghdr* CMSG_NXTHDR(struct msghdr* msg, struct cmsghdr* cmsg) | ||||||
|  | { | ||||||
|  |     struct cmsghdr* next = (struct cmsghdr*)((char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len)); | ||||||
|  |     unsigned offset = (char*)next - (char*)msg->msg_control; | ||||||
|  |     if (msg->msg_controllen < offset + sizeof(struct cmsghdr)) | ||||||
|  |         return (struct cmsghdr*)0; | ||||||
|  |     return next; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void* CMSG_DATA(struct cmsghdr* cmsg) | ||||||
|  | { | ||||||
|  |     return (void*)(cmsg + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct sockaddr { | struct sockaddr { | ||||||
|     sa_family_t sa_family; |     sa_family_t sa_family; | ||||||
|     char sa_data[14]; |     char sa_data[14]; | ||||||
|  |  | ||||||
|  | @ -520,6 +520,26 @@ ErrorOr<NonnullLockRefPtr<OpenFileDescription>> LocalSocket::recvfd(OpenFileDesc | ||||||
|     return queue.take_first(); |     return queue.take_first(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ErrorOr<NonnullLockRefPtrVector<OpenFileDescription>> LocalSocket::recvfds(OpenFileDescription const& socket_description, int n) | ||||||
|  | { | ||||||
|  |     MutexLocker locker(mutex()); | ||||||
|  |     NonnullLockRefPtrVector<OpenFileDescription> fds; | ||||||
|  | 
 | ||||||
|  |     auto role = this->role(socket_description); | ||||||
|  |     if (role != Role::Connected && role != Role::Accepted) | ||||||
|  |         return set_so_error(EINVAL); | ||||||
|  |     auto& queue = recvfd_queue_for(socket_description); | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i < n; ++i) { | ||||||
|  |         if (queue.is_empty()) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         fds.append(queue.take_first()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return fds; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ErrorOr<void> LocalSocket::try_set_path(StringView path) | ErrorOr<void> LocalSocket::try_set_path(StringView path) | ||||||
| { | { | ||||||
|     m_path = TRY(KString::try_create(path)); |     m_path = TRY(KString::try_create(path)); | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     ErrorOr<void> sendfd(OpenFileDescription const& socket_description, NonnullLockRefPtr<OpenFileDescription> passing_description); |     ErrorOr<void> sendfd(OpenFileDescription const& socket_description, NonnullLockRefPtr<OpenFileDescription> passing_description); | ||||||
|     ErrorOr<NonnullLockRefPtr<OpenFileDescription>> recvfd(OpenFileDescription const& socket_description); |     ErrorOr<NonnullLockRefPtr<OpenFileDescription>> recvfd(OpenFileDescription const& socket_description); | ||||||
|  |     ErrorOr<NonnullLockRefPtrVector<OpenFileDescription>> recvfds(OpenFileDescription const& socket_description, int n); | ||||||
| 
 | 
 | ||||||
|     static void for_each(Function<void(LocalSocket const&)>); |     static void for_each(Function<void(LocalSocket const&)>); | ||||||
|     static ErrorOr<void> try_for_each(Function<ErrorOr<void>(LocalSocket const&)>); |     static ErrorOr<void> try_for_each(Function<ErrorOr<void>(LocalSocket const&)>); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <AK/ByteBuffer.h> | ||||||
| #include <Kernel/FileSystem/OpenFileDescription.h> | #include <Kernel/FileSystem/OpenFileDescription.h> | ||||||
| #include <Kernel/Net/LocalSocket.h> | #include <Kernel/Net/LocalSocket.h> | ||||||
| #include <Kernel/Process.h> | #include <Kernel/Process.h> | ||||||
|  | @ -199,6 +200,24 @@ ErrorOr<FlatPtr> Process::sys$sendmsg(int sockfd, Userspace<const struct msghdr* | ||||||
|             Thread::current()->send_signal(SIGPIPE, &Process::current()); |             Thread::current()->send_signal(SIGPIPE, &Process::current()); | ||||||
|         return EPIPE; |         return EPIPE; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if (msg.msg_controllen > 0) { | ||||||
|  |         // Handle command messages.
 | ||||||
|  |         auto cmsg_buffer = TRY(ByteBuffer::create_uninitialized(msg.msg_controllen)); | ||||||
|  |         TRY(copy_from_user(cmsg_buffer.data(), msg.msg_control, msg.msg_controllen)); | ||||||
|  |         msg.msg_control = cmsg_buffer.data(); | ||||||
|  |         for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) { | ||||||
|  |             if (socket.is_local() && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { | ||||||
|  |                 auto& local_socket = static_cast<LocalSocket&>(socket); | ||||||
|  |                 int* fds = (int*)CMSG_DATA(cmsg); | ||||||
|  |                 size_t nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) / sizeof(int); | ||||||
|  |                 for (size_t i = 0; i < nfds; ++i) { | ||||||
|  |                     TRY(local_socket.sendfd(*description, TRY(open_file_description(fds[i])))); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     auto data_buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len)); |     auto data_buffer = TRY(UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len)); | ||||||
| 
 | 
 | ||||||
|     while (true) { |     while (true) { | ||||||
|  | @ -267,21 +286,41 @@ ErrorOr<FlatPtr> Process::sys$recvmsg(int sockfd, Userspace<struct msghdr*> user | ||||||
|         msg_flags |= MSG_TRUNC; |         msg_flags |= MSG_TRUNC; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (socket.wants_timestamp()) { |     socklen_t current_cmsg_len = 0; | ||||||
|         struct { |     auto try_add_cmsg = [&](int level, int type, void const* data, socklen_t len) -> ErrorOr<bool> { | ||||||
|             cmsghdr cmsg; |         if (current_cmsg_len + len > msg.msg_controllen) { | ||||||
|             timeval timestamp; |  | ||||||
|         } cmsg_timestamp; |  | ||||||
|         socklen_t control_length = sizeof(cmsg_timestamp); |  | ||||||
|         if (msg.msg_controllen < control_length) { |  | ||||||
|             msg_flags |= MSG_CTRUNC; |             msg_flags |= MSG_CTRUNC; | ||||||
|         } else { |             return false; | ||||||
|             cmsg_timestamp = { { control_length, SOL_SOCKET, SCM_TIMESTAMP }, timestamp.to_timeval() }; |  | ||||||
|             TRY(copy_to_user(msg.msg_control, &cmsg_timestamp, control_length)); |  | ||||||
|         } |         } | ||||||
|         TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_controllen, &control_length)); | 
 | ||||||
|  |         cmsghdr cmsg = { (socklen_t)CMSG_LEN(len), level, type }; | ||||||
|  |         cmsghdr* target = (cmsghdr*)(((char*)msg.msg_control) + current_cmsg_len); | ||||||
|  |         TRY(copy_to_user(target, &cmsg)); | ||||||
|  |         TRY(copy_to_user(CMSG_DATA(target), data, len)); | ||||||
|  |         current_cmsg_len += CMSG_ALIGN(cmsg.cmsg_len); | ||||||
|  |         return true; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (socket.wants_timestamp()) { | ||||||
|  |         timeval time = timestamp.to_timeval(); | ||||||
|  |         TRY(try_add_cmsg(SOL_SOCKET, SCM_TIMESTAMP, &time, sizeof(time))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     int space_for_fds = (msg.msg_controllen - current_cmsg_len - sizeof(struct cmsghdr)) / sizeof(int); | ||||||
|  |     if (space_for_fds > 0 && socket.is_local()) { | ||||||
|  |         auto& local_socket = static_cast<LocalSocket&>(socket); | ||||||
|  |         auto descriptions = TRY(local_socket.recvfds(description, space_for_fds)); | ||||||
|  |         Vector<int> fdnums; | ||||||
|  |         for (auto& description : descriptions) { | ||||||
|  |             auto fd_allocation = TRY(m_fds.with_exclusive([](auto& fds) { return fds.allocate(); })); | ||||||
|  |             m_fds.with_exclusive([&](auto& fds) { fds[fd_allocation.fd].set(description, 0); }); | ||||||
|  |             fdnums.append(fd_allocation.fd); | ||||||
|  |         } | ||||||
|  |         TRY(try_add_cmsg(SOL_SOCKET, SCM_RIGHTS, fdnums.data(), fdnums.size() * sizeof(int))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_controllen, ¤t_cmsg_len)); | ||||||
|  | 
 | ||||||
|     TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_flags, &msg_flags)); |     TRY(copy_to_user(&user_msg.unsafe_userspace_ptr()->msg_flags, &msg_flags)); | ||||||
|     return result.value(); |     return result.value(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -33,30 +33,4 @@ int socketpair(int domain, int type, int protocol, int sv[2]); | ||||||
| int sendfd(int sockfd, int fd); | int sendfd(int sockfd, int fd); | ||||||
| int recvfd(int sockfd, int options); | int recvfd(int sockfd, int options); | ||||||
| 
 | 
 | ||||||
| // These three are non-POSIX, but common:
 |  | ||||||
| #define CMSG_ALIGN(x) (((x) + sizeof(void*) - 1) & ~(sizeof(void*) - 1)) |  | ||||||
| #define CMSG_SPACE(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(x)) |  | ||||||
| #define CMSG_LEN(x) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (x)) |  | ||||||
| 
 |  | ||||||
| static inline struct cmsghdr* CMSG_FIRSTHDR(struct msghdr* msg) |  | ||||||
| { |  | ||||||
|     if (msg->msg_controllen < sizeof(struct cmsghdr)) |  | ||||||
|         return 0; |  | ||||||
|     return (struct cmsghdr*)msg->msg_control; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline struct cmsghdr* CMSG_NXTHDR(struct msghdr* msg, struct cmsghdr* cmsg) |  | ||||||
| { |  | ||||||
|     struct cmsghdr* next = (struct cmsghdr*)((char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len)); |  | ||||||
|     unsigned offset = (char*)next - (char*)msg->msg_control; |  | ||||||
|     if (msg->msg_controllen < offset + sizeof(struct cmsghdr)) |  | ||||||
|         return NULL; |  | ||||||
|     return next; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline void* CMSG_DATA(struct cmsghdr* cmsg) |  | ||||||
| { |  | ||||||
|     return (void*)(cmsg + 1); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| __END_DECLS | __END_DECLS | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Peter Elliott
						Peter Elliott