diff --git a/include/posix/sys/ioctl.h b/include/posix/sys/ioctl.h index 87d71fea5f9389..e69ab670fb84ee 100644 --- a/include/posix/sys/ioctl.h +++ b/include/posix/sys/ioctl.h @@ -8,7 +8,7 @@ #include -__syscall int sys_ioctl(int fd, unsigned long request, va_list args); +__syscall int sys_ioctl(int fd, unsigned long request, long n_args, uintptr_t *args); int ioctl(int fd, unsigned long request, ...); #define FIONBIO 0x5421 diff --git a/include/sys/fdtable.h b/include/sys/fdtable.h index 3f1aada23858df..4485609590de7f 100644 --- a/include/sys/fdtable.h +++ b/include/sys/fdtable.h @@ -22,7 +22,8 @@ extern "C" { struct fd_op_vtable { ssize_t (*read)(void *obj, void *buf, size_t sz); ssize_t (*write)(void *obj, const void *buf, size_t sz); - int (*ioctl)(void *obj, unsigned int request, va_list args); + int (*ioctl)(void *obj, unsigned long request, + long n_args, uintptr_t *args); }; /** @@ -113,14 +114,20 @@ void *z_get_fd_obj_and_vtable(int fd, const struct fd_op_vtable **vtable); * @param ... Variadic arguments to ioctl */ static inline int z_fdtable_call_ioctl(const struct fd_op_vtable *vtable, void *obj, - unsigned long request, ...) + unsigned long request, int n_args, ...) { - va_list args; - int res; - - va_start(args, request); - res = vtable->ioctl(obj, request, args); - va_end(args); + va_list varargs; + int i, res; + uintptr_t args[3]; + + __ASSERT_NO_MSG(n_args <= ARRAY_SIZE(args)); + + va_start(varargs, n_args); + for (i = 0; i < n_args; i++) { + args[i] = va_arg(varargs, uintptr_t); + } + res = vtable->ioctl(obj, request, n_args, args); + va_end(varargs); return res; } diff --git a/lib/os/fdtable.c b/lib/os/fdtable.c index e3055adfafd684..e08e75cbeeb140 100644 --- a/lib/os/fdtable.c +++ b/lib/os/fdtable.c @@ -24,6 +24,15 @@ #endif #include +/* Number of arguments which can be passed to public ioctl calls + * (i.e. from userspace to kernel space). + * Arbitrary value is supported (at the expense of stack usage). Can + * be increased when ioctl's with more arguments are added. + * Note that kernelspace-kernelspace ioctl calls are handled + * differently (in z_fdtable_call_ioctl()). + */ +#define MAX_USERSPACE_IOCTL_ARGS 1 + struct fd_entry { void *obj; const struct fd_op_vtable *vtable; @@ -238,7 +247,7 @@ int z_impl_sys_close(int fd) return -1; } - res = z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, ZFD_IOCTL_CLOSE); + res = z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, ZFD_IOCTL_CLOSE, 0); z_free_fd(fd); return res; @@ -264,7 +273,8 @@ int fsync(int fd) return -1; } - return z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, ZFD_IOCTL_FSYNC); + return z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, + ZFD_IOCTL_FSYNC, 0); } off_t lseek(int fd, off_t offset, int whence) @@ -273,51 +283,78 @@ off_t lseek(int fd, off_t offset, int whence) return -1; } - return z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, ZFD_IOCTL_LSEEK, - offset, whence); + return z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, + ZFD_IOCTL_LSEEK, + 2, offset, whence); } FUNC_ALIAS(lseek, _lseek, off_t); -int z_impl_sys_ioctl(int fd, unsigned long request, va_list args) +int z_impl_sys_ioctl(int fd, unsigned long request, long n_args, uintptr_t *args) { if (_check_fd(fd) < 0) { return -1; } - return fdtable[fd].vtable->ioctl(fdtable[fd].obj, request, args); + return fdtable[fd].vtable->ioctl(fdtable[fd].obj, request, n_args, args); } #ifdef CONFIG_USERSPACE -ssize_t z_vrfy_sys_ioctl(int fd, unsigned long request, va_list args) +ssize_t z_vrfy_sys_ioctl(int fd, unsigned long request, long n_args, uintptr_t *args) { -/* This doesn't work so far, until we start to fish inside a particular - * va_list structure for a particular arch. Maybe later. - */ -#if 0 - /* Check that we can access 4 words of arguments. This is very - * generic check, implementation of specific ioctl's are - * expected to perform their own detailed argument checking. - */ - Z_OOPS(Z_SYSCALL_MEMORY_READ(args, sizeof(uintptr_t) * 4)); -#endif + Z_OOPS(Z_SYSCALL_MEMORY_READ(args, sizeof(*args) * n_args)); if (request >= ZFD_IOCTL_PRIVATE) { errno = EINVAL; return -1; } - return z_impl_sys_ioctl(fd, request, args); + return z_impl_sys_ioctl(fd, request, n_args, args); } #include #endif /* CONFIG_USERSPACE */ +static int _vioctl(int fd, unsigned long request, va_list args) +{ + int i, n_args; + /* We assume that for argument passing [on stack], natural word size + * of the plaform is used. So for example, for LP64 platform, where + * int is 32-bit, it's still pushed as 64-bit value on stack. + */ + uintptr_t marshalled_args[MAX_USERSPACE_IOCTL_ARGS]; + + /* Calculate number of arguments for individual ioctl requests. */ + switch (request) { + case F_GETFL: + n_args = 0; + break; + case F_SETFL: + n_args = 1; + break; + default: + errno = EINVAL; + return -1; + } + + if (n_args > ARRAY_SIZE(marshalled_args)) { + /* Use distinguishable error code. */ + errno = EDOM; + return -1; + } + + for (i = 0; i < n_args; i++) { + marshalled_args[i] = va_arg(args, uintptr_t); + } + + return sys_ioctl(fd, request, n_args, marshalled_args); +} + int ioctl(int fd, unsigned long request, ...) { va_list args; int res; va_start(args, request); - res = sys_ioctl(fd, request, args); + res = _vioctl(fd, request, args); va_end(args); return res; @@ -344,7 +381,7 @@ int fcntl(int fd, int cmd, ...) /* The rest of commands are per-fd, handled by ioctl vmethod. */ va_start(args, cmd); - res = sys_ioctl(fd, cmd, args); + res = _vioctl(fd, cmd, args); va_end(args); return res; @@ -373,7 +410,8 @@ static ssize_t stdinout_write_vmeth(void *obj, const void *buffer, size_t count) #endif } -static int stdinout_ioctl_vmeth(void *obj, unsigned int request, va_list args) +static int stdinout_ioctl_vmeth(void *obj, unsigned long request, + long n_args, uintptr_t *args) { errno = EINVAL; return -1; diff --git a/lib/posix/eventfd.c b/lib/posix/eventfd.c index 27d9c9487d5774..1ddd26b57ac929 100644 --- a/lib/posix/eventfd.c +++ b/lib/posix/eventfd.c @@ -133,7 +133,8 @@ static ssize_t eventfd_write_op(void *obj, const void *buf, size_t sz) return sizeof(eventfd_t); } -static int eventfd_ioctl_op(void *obj, unsigned int request, va_list args) +static int eventfd_ioctl_op(void *obj, unsigned long request, + long n_args, uintptr_t *args) { struct eventfd *efd = (struct eventfd *)obj; @@ -144,7 +145,7 @@ static int eventfd_ioctl_op(void *obj, unsigned int request, va_list args) case F_SETFL: { int flags; - flags = va_arg(args, int); + flags = (int)args[0]; if (flags & ~EFD_FLAGS_SET) { errno = EINVAL; @@ -165,9 +166,9 @@ static int eventfd_ioctl_op(void *obj, unsigned int request, va_list args) struct k_poll_event **pev; struct k_poll_event *pev_end; - pfd = va_arg(args, struct zsock_pollfd *); - pev = va_arg(args, struct k_poll_event **); - pev_end = va_arg(args, struct k_poll_event *); + pfd = (struct zsock_pollfd *)args[0]; + pev = (struct k_poll_event **)args[1]; + pev_end = (struct k_poll_event *)args[2]; return eventfd_poll_prepare(obj, pfd, pev, pev_end); } @@ -176,8 +177,8 @@ static int eventfd_ioctl_op(void *obj, unsigned int request, va_list args) struct zsock_pollfd *pfd; struct k_poll_event **pev; - pfd = va_arg(args, struct zsock_pollfd *); - pev = va_arg(args, struct k_poll_event **); + pfd = (struct zsock_pollfd *)args[0]; + pev = (struct k_poll_event **)args[1]; return eventfd_poll_update(obj, pfd, pev); } diff --git a/lib/posix/fs.c b/lib/posix/fs.c index ba1ed9a9f78147..e81be5a8bf5032 100644 --- a/lib/posix/fs.c +++ b/lib/posix/fs.c @@ -93,7 +93,8 @@ int open(const char *name, int flags) return fd; } -static int fs_ioctl_vmeth(void *obj, unsigned int request, va_list args) +static int fs_ioctl_vmeth(void *obj, unsigned long request, + long n_args, uintptr_t *args) { int rc; struct posix_fs_desc *ptr = obj; @@ -108,8 +109,8 @@ static int fs_ioctl_vmeth(void *obj, unsigned int request, va_list args) off_t offset; int whence; - offset = va_arg(args, off_t); - whence = va_arg(args, int); + offset = (off_t)args[0]; + whence = (int)args[1]; rc = fs_seek(&ptr->file, offset, whence); break; diff --git a/subsys/net/lib/sockets/socketpair.c b/subsys/net/lib/sockets/socketpair.c index fb5be1e69be531..7e1abea42a7c2e 100644 --- a/subsys/net/lib/sockets/socketpair.c +++ b/subsys/net/lib/sockets/socketpair.c @@ -879,7 +879,8 @@ static int zsock_poll_update_ctx(struct spair *const spair, return res; } -static int spair_ioctl(void *obj, unsigned int request, va_list args) +static int spair_ioctl(void *obj, unsigned long request, + long n_args, uintptr_t *args) { int res; struct zsock_pollfd *pfd; @@ -915,7 +916,7 @@ static int spair_ioctl(void *obj, unsigned int request, va_list args) } case F_SETFL: { - flags = va_arg(args, int); + flags = (int)args[0]; if (flags & O_NONBLOCK) { spair->flags |= SPAIR_FLAG_NONBLOCK; @@ -936,17 +937,17 @@ static int spair_ioctl(void *obj, unsigned int request, va_list args) } case ZFD_IOCTL_POLL_PREPARE: { - pfd = va_arg(args, struct zsock_pollfd *); - pev = va_arg(args, struct k_poll_event **); - pev_end = va_arg(args, struct k_poll_event *); + pfd = (struct zsock_pollfd *)args[0]; + pev = (struct k_poll_event **)args[1]; + pev_end = (struct k_poll_event *)args[2]; res = zsock_poll_prepare_ctx(obj, pfd, pev, pev_end); goto out; } case ZFD_IOCTL_POLL_UPDATE: { - pfd = va_arg(args, struct zsock_pollfd *); - pev = va_arg(args, struct k_poll_event **); + pfd = (struct zsock_pollfd *)args[0]; + pev = (struct k_poll_event **)args[1]; res = zsock_poll_update_ctx(obj, pfd, pev); goto out; diff --git a/subsys/net/lib/sockets/sockets_can.c b/subsys/net/lib/sockets/sockets_can.c index e549bd73eb45ad..58778ee6f41d94 100644 --- a/subsys/net/lib/sockets/sockets_can.c +++ b/subsys/net/lib/sockets/sockets_can.c @@ -407,7 +407,8 @@ static int can_close_socket(struct net_context *ctx) return 0; } -static int can_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args) +static int can_sock_ioctl_vmeth(void *obj, unsigned long request, + long n_args, uintptr_t *args) { if (request == ZFD_IOCTL_CLOSE) { int ret; @@ -418,7 +419,7 @@ static int can_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args) } } - return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, args); + return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, n_args, args); } /* diff --git a/subsys/net/lib/sockets/sockets_net_mgmt.c b/subsys/net/lib/sockets/sockets_net_mgmt.c index 7f0fa646876f11..8f6c9ba8ab05c9 100644 --- a/subsys/net/lib/sockets/sockets_net_mgmt.c +++ b/subsys/net/lib/sockets/sockets_net_mgmt.c @@ -302,8 +302,8 @@ static ssize_t net_mgmt_sock_write(void *obj, const void *buffer, return znet_mgmt_sendto(obj, buffer, count, 0, NULL, 0); } -static int net_mgmt_sock_ioctl(void *obj, unsigned int request, - va_list args) +static int net_mgmt_sock_ioctl(void *obj, unsigned long request, + long n_args, uintptr_t *args) { return 0; } diff --git a/subsys/net/lib/sockets/sockets_packet.c b/subsys/net/lib/sockets/sockets_packet.c index 41fafd40d6a8c8..d91cf7d6050fa3 100644 --- a/subsys/net/lib/sockets/sockets_packet.c +++ b/subsys/net/lib/sockets/sockets_packet.c @@ -280,10 +280,10 @@ static ssize_t packet_sock_write_vmeth(void *obj, const void *buffer, return zpacket_sendto_ctx(obj, buffer, count, 0, NULL, 0); } -static int packet_sock_ioctl_vmeth(void *obj, unsigned int request, - va_list args) +static int packet_sock_ioctl_vmeth(void *obj, unsigned long request, + long n_args, uintptr_t *args) { - return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, args); + return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, n_args, args); } /* diff --git a/subsys/net/lib/sockets/sockets_tls.c b/subsys/net/lib/sockets/sockets_tls.c index a477317bd9c089..a6c9a28169e085 100644 --- a/subsys/net/lib/sockets/sockets_tls.c +++ b/subsys/net/lib/sockets/sockets_tls.c @@ -1936,7 +1936,8 @@ static ssize_t tls_sock_write_vmeth(void *obj, const void *buffer, return ztls_sendto_ctx(obj, buffer, count, 0, NULL, 0); } -static int tls_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args) +static int tls_sock_ioctl_vmeth(void *obj, unsigned long request, + long n_args, uintptr_t *args) { switch (request) { @@ -1945,7 +1946,8 @@ static int tls_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args) case F_SETFL: case ZFD_IOCTL_GETSOCKNAME: /* Pass the call to the core socket implementation. */ - return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, args); + return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, + n_args, args); case ZFD_IOCTL_CLOSE: return ztls_close_ctx(obj); @@ -1955,9 +1957,9 @@ static int tls_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args) struct k_poll_event **pev; struct k_poll_event *pev_end; - pfd = va_arg(args, struct zsock_pollfd *); - pev = va_arg(args, struct k_poll_event **); - pev_end = va_arg(args, struct k_poll_event *); + pfd = (struct zsock_pollfd *)args[0]; + pev = (struct k_poll_event **)args[1]; + pev_end = (struct k_poll_event *)args[2]; return ztls_poll_prepare_ctx(obj, pfd, pev, pev_end); } @@ -1966,8 +1968,8 @@ static int tls_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args) struct zsock_pollfd *pfd; struct k_poll_event **pev; - pfd = va_arg(args, struct zsock_pollfd *); - pev = va_arg(args, struct k_poll_event **); + pfd = (struct zsock_pollfd *)args[0]; + pev = (struct k_poll_event **)args[1]; return ztls_poll_update_ctx(obj, pfd, pev); } diff --git a/subsys/net/lib/websocket/websocket.c b/subsys/net/lib/websocket/websocket.c index 7019a02cacb8d2..0c12dd3c8e8dab 100644 --- a/subsys/net/lib/websocket/websocket.c +++ b/subsys/net/lib/websocket/websocket.c @@ -417,7 +417,8 @@ int websocket_disconnect(int ws_sock) return ret; } -static int websocket_ioctl_vmeth(void *obj, unsigned int request, va_list args) +static int websocket_ioctl_vmeth(void *obj, unsigned long request, + long n_args, uintptr_t *args) { if (request == ZFD_IOCTL_CLOSE) { struct websocket_context *ctx = obj; @@ -434,7 +435,7 @@ static int websocket_ioctl_vmeth(void *obj, unsigned int request, va_list args) return ret; } - return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, args); + return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, n_args, args); } static int websocket_prepare_and_send(struct websocket_context *ctx,