Skip to content

Commit

Permalink
lib: os: fdtable: Make ioctl method accept array of args instead of v…
Browse files Browse the repository at this point in the history
…a_list

As we cannot pass va_list across userspace/kernel boundary (because
we don't control its internel representation, and it may be quite,
complex), just get rid of it on the lowest level of ioctl machinery.
Pass just explicit array of machine words (uintptr_t) to the ioctl
vmethod implementation. On the API convenience layer, there's still
support for variadic arguments calls (both on the level of userspace
ioctl() function, and for internal in-kernel usage), with explicit
marshalling from va_list to array of uintptr_t's.

Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
  • Loading branch information
pfalcon committed Jun 11, 2020
1 parent e9944b9 commit 3e15b38
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 63 deletions.
2 changes: 1 addition & 1 deletion include/posix/sys/ioctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include <stdarg.h>

__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
Expand Down
23 changes: 15 additions & 8 deletions include/sys/fdtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

/**
Expand Down Expand Up @@ -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;
}
Expand Down
80 changes: 59 additions & 21 deletions lib/os/fdtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@
#endif
#include <syscall_handler.h>

/* 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;
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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 <syscalls/sys_ioctl_mrsh.c>
#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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 8 additions & 7 deletions lib/posix/eventfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
7 changes: 4 additions & 3 deletions lib/posix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
15 changes: 8 additions & 7 deletions subsys/net/lib/sockets/socketpair.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions subsys/net/lib/sockets/sockets_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

/*
Expand Down
4 changes: 2 additions & 2 deletions subsys/net/lib/sockets/sockets_net_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
6 changes: 3 additions & 3 deletions subsys/net/lib/sockets/sockets_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/*
Expand Down
16 changes: 9 additions & 7 deletions subsys/net/lib/sockets/sockets_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit 3e15b38

Please sign in to comment.