You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1015 lines
24 KiB
1015 lines
24 KiB
/* |
|
* Copyright (c) 2017 Linaro Limited |
|
* Copyright (c) 2021 Nordic Semiconductor |
|
* Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
/* Zephyr headers */ |
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(net_sock, CONFIG_NET_SOCKETS_LOG_LEVEL); |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/tracing/tracing.h> |
|
#include <zephyr/net/socket.h> |
|
#include <zephyr/internal/syscall_handler.h> |
|
|
|
#include "sockets_internal.h" |
|
|
|
#define VTABLE_CALL(fn, sock, ...) \ |
|
({ \ |
|
const struct socket_op_vtable *vtable; \ |
|
struct k_mutex *lock; \ |
|
void *obj; \ |
|
int retval; \ |
|
\ |
|
obj = get_sock_vtable(sock, &vtable, &lock); \ |
|
if (obj == NULL) { \ |
|
errno = EBADF; \ |
|
return -1; \ |
|
} \ |
|
\ |
|
if (vtable->fn == NULL) { \ |
|
errno = EOPNOTSUPP; \ |
|
return -1; \ |
|
} \ |
|
\ |
|
(void)k_mutex_lock(lock, K_FOREVER); \ |
|
\ |
|
retval = vtable->fn(obj, __VA_ARGS__); \ |
|
\ |
|
k_mutex_unlock(lock); \ |
|
\ |
|
retval; \ |
|
}) |
|
|
|
static inline void *get_sock_vtable(int sock, |
|
const struct socket_op_vtable **vtable, |
|
struct k_mutex **lock) |
|
{ |
|
void *ctx; |
|
|
|
ctx = zvfs_get_fd_obj_and_vtable(sock, |
|
(const struct fd_op_vtable **)vtable, |
|
lock); |
|
|
|
#ifdef CONFIG_USERSPACE |
|
if (ctx != NULL && k_is_in_user_syscall()) { |
|
if (!k_object_is_valid(ctx, K_OBJ_NET_SOCKET)) { |
|
/* Invalidate the context, the caller doesn't have |
|
* sufficient permission or there was some other |
|
* problem with the net socket object |
|
*/ |
|
ctx = NULL; |
|
} |
|
} |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
if (ctx == NULL) { |
|
NET_DBG("Invalid access on sock %d by thread %p (%s)", sock, |
|
_current, k_thread_name_get(_current)); |
|
} |
|
|
|
return ctx; |
|
} |
|
|
|
size_t msghdr_non_empty_iov_count(const struct msghdr *msg) |
|
{ |
|
size_t non_empty_iov_count = 0; |
|
|
|
for (size_t i = 0; i < msg->msg_iovlen; i++) { |
|
if (msg->msg_iov[i].iov_len) { |
|
non_empty_iov_count++; |
|
} |
|
} |
|
|
|
return non_empty_iov_count; |
|
} |
|
|
|
void *z_impl_zsock_get_context_object(int sock) |
|
{ |
|
const struct socket_op_vtable *ignored; |
|
|
|
return get_sock_vtable(sock, &ignored, NULL); |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
void *z_vrfy_zsock_get_context_object(int sock) |
|
{ |
|
/* All checking done in implementation */ |
|
return z_impl_zsock_get_context_object(sock); |
|
} |
|
|
|
#include <zephyr/syscalls/zsock_get_context_object_mrsh.c> |
|
#endif |
|
|
|
int z_impl_zsock_socket(int family, int type, int proto) |
|
{ |
|
STRUCT_SECTION_FOREACH(net_socket_register, sock_family) { |
|
int ret; |
|
|
|
if (sock_family->family != family && |
|
sock_family->family != AF_UNSPEC) { |
|
continue; |
|
} |
|
|
|
NET_ASSERT(sock_family->is_supported); |
|
|
|
if (!sock_family->is_supported(family, type, proto)) { |
|
continue; |
|
} |
|
|
|
errno = 0; |
|
ret = sock_family->handler(family, type, proto); |
|
|
|
SYS_PORT_TRACING_OBJ_INIT(socket, ret < 0 ? -errno : ret, |
|
family, type, proto); |
|
|
|
(void)sock_obj_core_alloc(ret, sock_family, family, type, proto); |
|
|
|
return ret; |
|
} |
|
|
|
errno = EAFNOSUPPORT; |
|
SYS_PORT_TRACING_OBJ_INIT(socket, -errno, family, type, proto); |
|
return -1; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_socket(int family, int type, int proto) |
|
{ |
|
/* implementation call to net_context_get() should do all necessary |
|
* checking |
|
*/ |
|
return z_impl_zsock_socket(family, type, proto); |
|
} |
|
#include <zephyr/syscalls/zsock_socket_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
extern int zvfs_close(int fd); |
|
|
|
int z_impl_zsock_close(int sock) |
|
{ |
|
return zvfs_close(sock); |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_close(int sock) |
|
{ |
|
const struct socket_op_vtable *vtable; |
|
struct k_mutex *lock; |
|
void *ctx; |
|
|
|
ctx = get_sock_vtable(sock, &vtable, &lock); |
|
if (ctx == NULL) { |
|
errno = EBADF; |
|
return -1; |
|
} |
|
|
|
return z_impl_zsock_close(sock); |
|
} |
|
#include <zephyr/syscalls/zsock_close_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_shutdown(int sock, int how) |
|
{ |
|
const struct socket_op_vtable *vtable; |
|
struct k_mutex *lock; |
|
void *ctx; |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, shutdown, sock, how); |
|
|
|
ctx = get_sock_vtable(sock, &vtable, &lock); |
|
if (ctx == NULL) { |
|
errno = EBADF; |
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, shutdown, sock, -errno); |
|
return -1; |
|
} |
|
|
|
if (!vtable->shutdown) { |
|
errno = ENOTSUP; |
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, shutdown, sock, -errno); |
|
return -1; |
|
} |
|
|
|
(void)k_mutex_lock(lock, K_FOREVER); |
|
|
|
NET_DBG("shutdown: ctx=%p, fd=%d, how=%d", ctx, sock, how); |
|
|
|
ret = vtable->shutdown(ctx, how); |
|
|
|
k_mutex_unlock(lock); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, shutdown, sock, ret < 0 ? -errno : ret); |
|
|
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_shutdown(int sock, int how) |
|
{ |
|
return z_impl_zsock_shutdown(sock, how); |
|
} |
|
#include <zephyr/syscalls/zsock_shutdown_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_bind(int sock, const struct sockaddr *addr, socklen_t addrlen) |
|
{ |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, bind, sock, addr, addrlen); |
|
|
|
ret = VTABLE_CALL(bind, sock, addr, addrlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, bind, sock, ret < 0 ? -errno : ret); |
|
|
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_bind(int sock, const struct sockaddr *addr, |
|
socklen_t addrlen) |
|
{ |
|
struct sockaddr_storage dest_addr_copy; |
|
|
|
K_OOPS(K_SYSCALL_VERIFY(addrlen <= sizeof(dest_addr_copy))); |
|
K_OOPS(k_usermode_from_copy(&dest_addr_copy, (void *)addr, addrlen)); |
|
|
|
return z_impl_zsock_bind(sock, (struct sockaddr *)&dest_addr_copy, |
|
addrlen); |
|
} |
|
#include <zephyr/syscalls/zsock_bind_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_connect(int sock, const struct sockaddr *addr, |
|
socklen_t addrlen) |
|
{ |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, connect, sock, addr, addrlen); |
|
|
|
ret = VTABLE_CALL(connect, sock, addr, addrlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, connect, sock, |
|
ret < 0 ? -errno : ret); |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
int z_vrfy_zsock_connect(int sock, const struct sockaddr *addr, |
|
socklen_t addrlen) |
|
{ |
|
struct sockaddr_storage dest_addr_copy; |
|
|
|
K_OOPS(K_SYSCALL_VERIFY(addrlen <= sizeof(dest_addr_copy))); |
|
K_OOPS(k_usermode_from_copy(&dest_addr_copy, (void *)addr, addrlen)); |
|
|
|
return z_impl_zsock_connect(sock, (struct sockaddr *)&dest_addr_copy, |
|
addrlen); |
|
} |
|
#include <zephyr/syscalls/zsock_connect_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_listen(int sock, int backlog) |
|
{ |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, listen, sock, backlog); |
|
|
|
ret = VTABLE_CALL(listen, sock, backlog); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, listen, sock, |
|
ret < 0 ? -errno : ret); |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_listen(int sock, int backlog) |
|
{ |
|
return z_impl_zsock_listen(sock, backlog); |
|
} |
|
#include <zephyr/syscalls/zsock_listen_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen) |
|
{ |
|
int new_sock; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, accept, sock); |
|
|
|
new_sock = VTABLE_CALL(accept, sock, addr, addrlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, accept, new_sock, addr, addrlen, |
|
new_sock < 0 ? -errno : 0); |
|
|
|
(void)sock_obj_core_alloc_find(sock, new_sock, SOCK_STREAM); |
|
|
|
return new_sock; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_accept(int sock, struct sockaddr *addr, |
|
socklen_t *addrlen) |
|
{ |
|
socklen_t addrlen_copy; |
|
int ret; |
|
|
|
K_OOPS(addrlen && k_usermode_from_copy(&addrlen_copy, addrlen, |
|
sizeof(socklen_t))); |
|
K_OOPS(addr && K_SYSCALL_MEMORY_WRITE(addr, addrlen ? addrlen_copy : 0)); |
|
|
|
ret = z_impl_zsock_accept(sock, (struct sockaddr *)addr, |
|
addrlen ? &addrlen_copy : NULL); |
|
|
|
K_OOPS(ret >= 0 && addrlen && k_usermode_to_copy(addrlen, &addrlen_copy, |
|
sizeof(socklen_t))); |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zsock_accept_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
ssize_t z_impl_zsock_sendto(int sock, const void *buf, size_t len, int flags, |
|
const struct sockaddr *dest_addr, socklen_t addrlen) |
|
{ |
|
int bytes_sent; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, sendto, sock, len, flags, |
|
dest_addr, addrlen); |
|
|
|
bytes_sent = VTABLE_CALL(sendto, sock, buf, len, flags, dest_addr, addrlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, sendto, sock, |
|
bytes_sent < 0 ? -errno : bytes_sent); |
|
|
|
sock_obj_core_update_send_stats(sock, bytes_sent); |
|
|
|
return bytes_sent; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
ssize_t z_vrfy_zsock_sendto(int sock, const void *buf, size_t len, int flags, |
|
const struct sockaddr *dest_addr, socklen_t addrlen) |
|
{ |
|
struct sockaddr_storage dest_addr_copy; |
|
|
|
K_OOPS(K_SYSCALL_MEMORY_READ(buf, len)); |
|
if (dest_addr) { |
|
K_OOPS(K_SYSCALL_VERIFY(addrlen <= sizeof(dest_addr_copy))); |
|
K_OOPS(k_usermode_from_copy(&dest_addr_copy, (void *)dest_addr, |
|
addrlen)); |
|
} |
|
|
|
return z_impl_zsock_sendto(sock, (const void *)buf, len, flags, |
|
dest_addr ? (struct sockaddr *)&dest_addr_copy : NULL, |
|
addrlen); |
|
} |
|
#include <zephyr/syscalls/zsock_sendto_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
ssize_t z_impl_zsock_sendmsg(int sock, const struct msghdr *msg, int flags) |
|
{ |
|
int bytes_sent; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, sendmsg, sock, msg, flags); |
|
|
|
bytes_sent = VTABLE_CALL(sendmsg, sock, msg, flags); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, sendmsg, sock, |
|
bytes_sent < 0 ? -errno : bytes_sent); |
|
|
|
sock_obj_core_update_send_stats(sock, bytes_sent); |
|
|
|
return bytes_sent; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline ssize_t z_vrfy_zsock_sendmsg(int sock, |
|
const struct msghdr *msg, |
|
int flags) |
|
{ |
|
struct msghdr msg_copy; |
|
size_t i; |
|
int ret; |
|
|
|
K_OOPS(k_usermode_from_copy(&msg_copy, (void *)msg, sizeof(msg_copy))); |
|
|
|
msg_copy.msg_name = NULL; |
|
msg_copy.msg_control = NULL; |
|
|
|
msg_copy.msg_iov = k_usermode_alloc_from_copy(msg->msg_iov, |
|
msg->msg_iovlen * sizeof(struct iovec)); |
|
if (!msg_copy.msg_iov) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
|
|
for (i = 0; i < msg->msg_iovlen; i++) { |
|
msg_copy.msg_iov[i].iov_base = |
|
k_usermode_alloc_from_copy(msg->msg_iov[i].iov_base, |
|
msg->msg_iov[i].iov_len); |
|
if (!msg_copy.msg_iov[i].iov_base) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
|
|
msg_copy.msg_iov[i].iov_len = msg->msg_iov[i].iov_len; |
|
} |
|
|
|
if (msg->msg_namelen > 0) { |
|
msg_copy.msg_name = k_usermode_alloc_from_copy(msg->msg_name, |
|
msg->msg_namelen); |
|
if (!msg_copy.msg_name) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
} |
|
|
|
if (msg->msg_controllen > 0) { |
|
msg_copy.msg_control = k_usermode_alloc_from_copy(msg->msg_control, |
|
msg->msg_controllen); |
|
if (!msg_copy.msg_control) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
} |
|
|
|
ret = z_impl_zsock_sendmsg(sock, (const struct msghdr *)&msg_copy, |
|
flags); |
|
|
|
k_free(msg_copy.msg_name); |
|
k_free(msg_copy.msg_control); |
|
|
|
for (i = 0; i < msg_copy.msg_iovlen; i++) { |
|
k_free(msg_copy.msg_iov[i].iov_base); |
|
} |
|
|
|
k_free(msg_copy.msg_iov); |
|
|
|
return ret; |
|
|
|
fail: |
|
if (msg_copy.msg_name) { |
|
k_free(msg_copy.msg_name); |
|
} |
|
|
|
if (msg_copy.msg_control) { |
|
k_free(msg_copy.msg_control); |
|
} |
|
|
|
if (msg_copy.msg_iov) { |
|
for (i = 0; i < msg_copy.msg_iovlen; i++) { |
|
if (msg_copy.msg_iov[i].iov_base) { |
|
k_free(msg_copy.msg_iov[i].iov_base); |
|
} |
|
} |
|
|
|
k_free(msg_copy.msg_iov); |
|
} |
|
|
|
return -1; |
|
} |
|
#include <zephyr/syscalls/zsock_sendmsg_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
ssize_t z_impl_zsock_recvfrom(int sock, void *buf, size_t max_len, int flags, |
|
struct sockaddr *src_addr, socklen_t *addrlen) |
|
{ |
|
int bytes_received; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, recvfrom, sock, max_len, flags, src_addr, addrlen); |
|
|
|
bytes_received = VTABLE_CALL(recvfrom, sock, buf, max_len, flags, src_addr, addrlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, recvfrom, sock, |
|
src_addr, addrlen, |
|
bytes_received < 0 ? -errno : bytes_received); |
|
|
|
sock_obj_core_update_recv_stats(sock, bytes_received); |
|
|
|
return bytes_received; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
ssize_t z_vrfy_zsock_recvfrom(int sock, void *buf, size_t max_len, int flags, |
|
struct sockaddr *src_addr, socklen_t *addrlen) |
|
{ |
|
socklen_t addrlen_copy; |
|
ssize_t ret; |
|
|
|
if (K_SYSCALL_MEMORY_WRITE(buf, max_len)) { |
|
errno = EFAULT; |
|
return -1; |
|
} |
|
|
|
if (addrlen) { |
|
K_OOPS(k_usermode_from_copy(&addrlen_copy, addrlen, |
|
sizeof(socklen_t))); |
|
} |
|
K_OOPS(src_addr && K_SYSCALL_MEMORY_WRITE(src_addr, addrlen_copy)); |
|
|
|
ret = z_impl_zsock_recvfrom(sock, (void *)buf, max_len, flags, |
|
(struct sockaddr *)src_addr, |
|
addrlen ? &addrlen_copy : NULL); |
|
|
|
if (addrlen) { |
|
K_OOPS(k_usermode_to_copy(addrlen, &addrlen_copy, |
|
sizeof(socklen_t))); |
|
} |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zsock_recvfrom_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
ssize_t z_impl_zsock_recvmsg(int sock, struct msghdr *msg, int flags) |
|
{ |
|
int bytes_received; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, recvmsg, sock, msg, flags); |
|
|
|
bytes_received = VTABLE_CALL(recvmsg, sock, msg, flags); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, recvmsg, sock, msg, |
|
bytes_received < 0 ? -errno : bytes_received); |
|
|
|
sock_obj_core_update_recv_stats(sock, bytes_received); |
|
|
|
return bytes_received; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
ssize_t z_vrfy_zsock_recvmsg(int sock, struct msghdr *msg, int flags) |
|
{ |
|
struct msghdr msg_copy; |
|
size_t iovlen; |
|
size_t i; |
|
int ret; |
|
|
|
if (msg == NULL) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
if (msg->msg_iov == NULL) { |
|
errno = ENOMEM; |
|
return -1; |
|
} |
|
|
|
K_OOPS(k_usermode_from_copy(&msg_copy, (void *)msg, sizeof(msg_copy))); |
|
|
|
k_usermode_from_copy(&iovlen, &msg->msg_iovlen, sizeof(iovlen)); |
|
|
|
msg_copy.msg_name = NULL; |
|
msg_copy.msg_control = NULL; |
|
|
|
msg_copy.msg_iov = k_usermode_alloc_from_copy(msg->msg_iov, |
|
msg->msg_iovlen * sizeof(struct iovec)); |
|
if (!msg_copy.msg_iov) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
|
|
/* Clear the pointers in the copy so that if the allocation in the |
|
* next loop fails, we do not try to free non allocated memory |
|
* in fail branch. |
|
*/ |
|
memset(msg_copy.msg_iov, 0, msg->msg_iovlen * sizeof(struct iovec)); |
|
|
|
for (i = 0; i < iovlen; i++) { |
|
/* TODO: In practice we do not need to copy the actual data |
|
* in msghdr when receiving data but currently there is no |
|
* ready made function to do just that (unless we want to call |
|
* relevant malloc function here ourselves). So just use |
|
* the copying variant for now. |
|
*/ |
|
msg_copy.msg_iov[i].iov_base = |
|
k_usermode_alloc_from_copy(msg->msg_iov[i].iov_base, |
|
msg->msg_iov[i].iov_len); |
|
if (!msg_copy.msg_iov[i].iov_base) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
|
|
msg_copy.msg_iov[i].iov_len = msg->msg_iov[i].iov_len; |
|
} |
|
|
|
if (msg->msg_namelen > 0) { |
|
if (msg->msg_name == NULL) { |
|
errno = EINVAL; |
|
goto fail; |
|
} |
|
|
|
msg_copy.msg_name = k_usermode_alloc_from_copy(msg->msg_name, |
|
msg->msg_namelen); |
|
if (msg_copy.msg_name == NULL) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
} |
|
|
|
if (msg->msg_controllen > 0) { |
|
if (msg->msg_control == NULL) { |
|
errno = EINVAL; |
|
goto fail; |
|
} |
|
|
|
msg_copy.msg_control = |
|
k_usermode_alloc_from_copy(msg->msg_control, |
|
msg->msg_controllen); |
|
if (msg_copy.msg_control == NULL) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
} |
|
|
|
ret = z_impl_zsock_recvmsg(sock, &msg_copy, flags); |
|
|
|
/* Do not copy anything back if there was an error or nothing was |
|
* received. |
|
*/ |
|
if (ret > 0) { |
|
if (msg->msg_namelen > 0 && msg->msg_name != NULL) { |
|
K_OOPS(k_usermode_to_copy(msg->msg_name, |
|
msg_copy.msg_name, |
|
msg_copy.msg_namelen)); |
|
} |
|
|
|
if (msg->msg_controllen > 0 && |
|
msg->msg_control != NULL) { |
|
K_OOPS(k_usermode_to_copy(msg->msg_control, |
|
msg_copy.msg_control, |
|
msg_copy.msg_controllen)); |
|
|
|
msg->msg_controllen = msg_copy.msg_controllen; |
|
} else { |
|
msg->msg_controllen = 0U; |
|
} |
|
|
|
k_usermode_to_copy(&msg->msg_iovlen, |
|
&msg_copy.msg_iovlen, |
|
sizeof(msg->msg_iovlen)); |
|
|
|
/* The new iovlen cannot be bigger than the original one */ |
|
NET_ASSERT(msg_copy.msg_iovlen <= iovlen); |
|
|
|
for (i = 0; i < iovlen; i++) { |
|
if (i < msg_copy.msg_iovlen) { |
|
K_OOPS(k_usermode_to_copy(msg->msg_iov[i].iov_base, |
|
msg_copy.msg_iov[i].iov_base, |
|
msg_copy.msg_iov[i].iov_len)); |
|
K_OOPS(k_usermode_to_copy(&msg->msg_iov[i].iov_len, |
|
&msg_copy.msg_iov[i].iov_len, |
|
sizeof(msg->msg_iov[i].iov_len))); |
|
} else { |
|
/* Clear out those vectors that we could not populate */ |
|
msg->msg_iov[i].iov_len = 0; |
|
} |
|
} |
|
|
|
k_usermode_to_copy(&msg->msg_flags, |
|
&msg_copy.msg_flags, |
|
sizeof(msg->msg_flags)); |
|
} |
|
|
|
k_free(msg_copy.msg_name); |
|
k_free(msg_copy.msg_control); |
|
|
|
/* Note that we need to free according to original iovlen */ |
|
for (i = 0; i < iovlen; i++) { |
|
k_free(msg_copy.msg_iov[i].iov_base); |
|
} |
|
|
|
k_free(msg_copy.msg_iov); |
|
|
|
return ret; |
|
|
|
fail: |
|
if (msg_copy.msg_name) { |
|
k_free(msg_copy.msg_name); |
|
} |
|
|
|
if (msg_copy.msg_control) { |
|
k_free(msg_copy.msg_control); |
|
} |
|
|
|
if (msg_copy.msg_iov) { |
|
for (i = 0; i < msg_copy.msg_iovlen; i++) { |
|
if (msg_copy.msg_iov[i].iov_base) { |
|
k_free(msg_copy.msg_iov[i].iov_base); |
|
} |
|
} |
|
|
|
k_free(msg_copy.msg_iov); |
|
} |
|
|
|
return -1; |
|
} |
|
#include <zephyr/syscalls/zsock_recvmsg_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
/* As this is limited function, we don't follow POSIX signature, with |
|
* "..." instead of last arg. |
|
*/ |
|
int z_impl_zsock_fcntl_impl(int sock, int cmd, int flags) |
|
{ |
|
const struct socket_op_vtable *vtable; |
|
struct k_mutex *lock; |
|
void *obj; |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, fcntl, sock, cmd, flags); |
|
|
|
obj = get_sock_vtable(sock, &vtable, &lock); |
|
if (obj == NULL) { |
|
errno = EBADF; |
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, fcntl, sock, -errno); |
|
return -1; |
|
} |
|
|
|
(void)k_mutex_lock(lock, K_FOREVER); |
|
|
|
ret = zvfs_fdtable_call_ioctl((const struct fd_op_vtable *)vtable, |
|
obj, cmd, flags); |
|
|
|
k_mutex_unlock(lock); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, fcntl, sock, |
|
ret < 0 ? -errno : ret); |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_fcntl_impl(int sock, int cmd, int flags) |
|
{ |
|
return z_impl_zsock_fcntl_impl(sock, cmd, flags); |
|
} |
|
#include <zephyr/syscalls/zsock_fcntl_impl_mrsh.c> |
|
#endif |
|
|
|
int z_impl_zsock_ioctl_impl(int sock, unsigned long request, va_list args) |
|
{ |
|
const struct socket_op_vtable *vtable; |
|
struct k_mutex *lock; |
|
void *ctx; |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, ioctl, sock, request); |
|
|
|
ctx = get_sock_vtable(sock, &vtable, &lock); |
|
if (ctx == NULL) { |
|
errno = EBADF; |
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, ioctl, sock, -errno); |
|
return -1; |
|
} |
|
|
|
(void)k_mutex_lock(lock, K_FOREVER); |
|
|
|
NET_DBG("ioctl: ctx=%p, fd=%d, request=%lu", ctx, sock, request); |
|
|
|
ret = vtable->fd_vtable.ioctl(ctx, request, args); |
|
|
|
k_mutex_unlock(lock); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, ioctl, sock, |
|
ret < 0 ? -errno : ret); |
|
return ret; |
|
|
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_ioctl_impl(int sock, unsigned long request, va_list args) |
|
{ |
|
switch (request) { |
|
case ZFD_IOCTL_FIONBIO: |
|
break; |
|
|
|
case ZFD_IOCTL_FIONREAD: { |
|
int *avail; |
|
|
|
avail = va_arg(args, int *); |
|
K_OOPS(K_SYSCALL_MEMORY_WRITE(avail, sizeof(*avail))); |
|
|
|
break; |
|
} |
|
|
|
default: |
|
errno = EOPNOTSUPP; |
|
return -1; |
|
} |
|
|
|
return z_impl_zsock_ioctl_impl(sock, request, args); |
|
} |
|
#include <zephyr/syscalls/zsock_ioctl_impl_mrsh.c> |
|
#endif |
|
|
|
int z_impl_zsock_inet_pton(sa_family_t family, const char *src, void *dst) |
|
{ |
|
if (net_addr_pton(family, src, dst) == 0) { |
|
return 1; |
|
} else { |
|
return 0; |
|
} |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_inet_pton(sa_family_t family, |
|
const char *src, void *dst) |
|
{ |
|
int dst_size; |
|
char src_copy[NET_IPV6_ADDR_LEN]; |
|
char dst_copy[sizeof(struct in6_addr)]; |
|
int ret; |
|
|
|
switch (family) { |
|
case AF_INET: |
|
dst_size = sizeof(struct in_addr); |
|
break; |
|
case AF_INET6: |
|
dst_size = sizeof(struct in6_addr); |
|
break; |
|
default: |
|
errno = EAFNOSUPPORT; |
|
return -1; |
|
} |
|
|
|
K_OOPS(k_usermode_string_copy(src_copy, (char *)src, sizeof(src_copy))); |
|
ret = z_impl_zsock_inet_pton(family, src_copy, dst_copy); |
|
K_OOPS(k_usermode_to_copy(dst, dst_copy, dst_size)); |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zsock_inet_pton_mrsh.c> |
|
#endif |
|
|
|
int z_impl_zsock_getsockopt(int sock, int level, int optname, |
|
void *optval, socklen_t *optlen) |
|
{ |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, getsockopt, sock, level, optname); |
|
|
|
ret = VTABLE_CALL(getsockopt, sock, level, optname, optval, optlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, getsockopt, sock, level, optname, |
|
optval, *optlen, ret < 0 ? -errno : ret); |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
int z_vrfy_zsock_getsockopt(int sock, int level, int optname, |
|
void *optval, socklen_t *optlen) |
|
{ |
|
socklen_t kernel_optlen = *(socklen_t *)optlen; |
|
void *kernel_optval; |
|
int ret; |
|
|
|
if (K_SYSCALL_MEMORY_WRITE(optval, kernel_optlen)) { |
|
errno = -EPERM; |
|
return -1; |
|
} |
|
|
|
kernel_optval = k_usermode_alloc_from_copy((const void *)optval, |
|
kernel_optlen); |
|
K_OOPS(!kernel_optval); |
|
|
|
ret = z_impl_zsock_getsockopt(sock, level, optname, |
|
kernel_optval, &kernel_optlen); |
|
|
|
K_OOPS(k_usermode_to_copy((void *)optval, kernel_optval, kernel_optlen)); |
|
K_OOPS(k_usermode_to_copy((void *)optlen, &kernel_optlen, |
|
sizeof(socklen_t))); |
|
|
|
k_free(kernel_optval); |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zsock_getsockopt_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_setsockopt(int sock, int level, int optname, |
|
const void *optval, socklen_t optlen) |
|
{ |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, setsockopt, sock, |
|
level, optname, optval, optlen); |
|
|
|
ret = VTABLE_CALL(setsockopt, sock, level, optname, optval, optlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, setsockopt, sock, |
|
ret < 0 ? -errno : ret); |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
int z_vrfy_zsock_setsockopt(int sock, int level, int optname, |
|
const void *optval, socklen_t optlen) |
|
{ |
|
void *kernel_optval; |
|
int ret; |
|
|
|
kernel_optval = k_usermode_alloc_from_copy((const void *)optval, optlen); |
|
K_OOPS(!kernel_optval); |
|
|
|
ret = z_impl_zsock_setsockopt(sock, level, optname, |
|
kernel_optval, optlen); |
|
|
|
k_free(kernel_optval); |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zsock_setsockopt_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_getpeername(int sock, struct sockaddr *addr, |
|
socklen_t *addrlen) |
|
{ |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, getpeername, sock); |
|
|
|
ret = VTABLE_CALL(getpeername, sock, addr, addrlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, getpeername, sock, |
|
addr, addrlen, |
|
ret < 0 ? -errno : ret); |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_getpeername(int sock, struct sockaddr *addr, |
|
socklen_t *addrlen) |
|
{ |
|
socklen_t addrlen_copy; |
|
int ret; |
|
|
|
K_OOPS(k_usermode_from_copy(&addrlen_copy, (void *)addrlen, |
|
sizeof(socklen_t))); |
|
|
|
if (K_SYSCALL_MEMORY_WRITE(addr, addrlen_copy)) { |
|
errno = EFAULT; |
|
return -1; |
|
} |
|
|
|
ret = z_impl_zsock_getpeername(sock, (struct sockaddr *)addr, |
|
&addrlen_copy); |
|
|
|
if (ret == 0 && |
|
k_usermode_to_copy((void *)addrlen, &addrlen_copy, |
|
sizeof(socklen_t))) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zsock_getpeername_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */ |
|
|
|
int z_impl_zsock_getsockname(int sock, struct sockaddr *addr, |
|
socklen_t *addrlen) |
|
{ |
|
int ret; |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(socket, getsockname, sock); |
|
|
|
ret = VTABLE_CALL(getsockname, sock, addr, addrlen); |
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(socket, getsockname, sock, |
|
addr, addrlen, |
|
ret < 0 ? -errno : ret); |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline int z_vrfy_zsock_getsockname(int sock, struct sockaddr *addr, |
|
socklen_t *addrlen) |
|
{ |
|
socklen_t addrlen_copy; |
|
int ret; |
|
|
|
K_OOPS(k_usermode_from_copy(&addrlen_copy, (void *)addrlen, |
|
sizeof(socklen_t))); |
|
|
|
if (K_SYSCALL_MEMORY_WRITE(addr, addrlen_copy)) { |
|
errno = EFAULT; |
|
return -1; |
|
} |
|
|
|
ret = z_impl_zsock_getsockname(sock, (struct sockaddr *)addr, |
|
&addrlen_copy); |
|
|
|
if (ret == 0 && |
|
k_usermode_to_copy((void *)addrlen, &addrlen_copy, |
|
sizeof(socklen_t))) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zsock_getsockname_mrsh.c> |
|
#endif /* CONFIG_USERSPACE */
|
|
|