|
Message-ID: <20170621010811.GC1627@brightrain.aerifal.cx> Date: Tue, 20 Jun 2017 21:08:11 -0400 From: Rich Felker <dalias@...c.org> To: musl@...ts.openwall.com Subject: Re: [PATCH] Be more precise using va_arg in fcntl On Mon, Jun 19, 2017 at 09:10:43PM +0300, Omer Anson wrote: > Currently, fcntl automatically calls va_arg with type unsigned long, > regardless of the actual invoked command, and therefore ignoring whether > such an argument exists, or its type. > > According to the C standard[1][2], calling va_arg if the types do not match, > or beyond the end of the parameter list, causes an undefined behaviour. > > This change modifies fcntl to try and call va_arg only when necessary, > and with the correct parameter type. It relies on the cmd argument to > know what's the expected command. In case the cmd is unknown, it falls > back to the previous behaviour. > > [1] http://www.open-std.org/jtc1/sc22/wg14/www/standards > [2] http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdarg.h.html Thanks for bringing this up again. I've actually worked on this at least twice in the past, and the work got set aside because of bikeshedding and/or theoretical concerns about how to handle unknown commands or something -- but it appears all the discussion was on IRC; I can't find it on the list. I do have some of my old versions of the patch, but I'd need to cross-reference them against commits with later dates because some related changes (making pointers get passed right to kernel on x32, I think) that overlap with my patches were already committed. Some comments: > --- > src/fcntl/fcntl.c | 72 ++++++++++++++++++++++++++++++++++++++++++------------- > 1 file changed, 56 insertions(+), 16 deletions(-) > > diff --git a/src/fcntl/fcntl.c b/src/fcntl/fcntl.c > index ce615d0..9d6b2cc 100644 > --- a/src/fcntl/fcntl.c > +++ b/src/fcntl/fcntl.c > @@ -5,24 +5,40 @@ > #include "syscall.h" > #include "libc.h" > > +#define GET_ARG_MACRO(type) ({ \ > + va_list ap; \ > + va_start(ap, cmd); \ > + type __arg = va_arg(ap, type); \ > + va_end(ap); \ > + __arg; \ > + }) > + This macro uses GNU C statement-expressions, which aren't allowed in musl, but it also seems mostly gratuitous. The first 2 lines can just be at the top of the function as before; only the va_end then ends up being repeate when expanding it manually. > int fcntl(int fd, int cmd, ...) > { > - unsigned long arg; > - va_list ap; > - va_start(ap, cmd); > - arg = va_arg(ap, unsigned long); > - va_end(ap); > - if (cmd == F_SETFL) arg |= O_LARGEFILE; > - if (cmd == F_SETLKW) return syscall_cp(SYS_fcntl, fd, cmd, (void *)arg); > - if (cmd == F_GETOWN) { > + switch (cmd) { > + case F_GETFD: > + case F_GETFL: > + case F_GETSIG: > + case F_GETLEASE: > + case F_GET_SEALS: > + case F_GETPIPE_SZ: > + return syscall(SYS_fcntl, fd, cmd); > + case F_GETOWN: { > struct f_owner_ex ex; > int ret = __syscall(SYS_fcntl, fd, F_GETOWN_EX, &ex); > - if (ret == -EINVAL) return __syscall(SYS_fcntl, fd, cmd, (void *)arg); > + if (ret == -EINVAL) return __syscall(SYS_fcntl, fd, cmd); > if (ret) return __syscall_ret(ret); > return ex.type == F_OWNER_PGRP ? -ex.pid : ex.pid; > } > - if (cmd == F_DUPFD_CLOEXEC) { > - int ret = __syscall(SYS_fcntl, fd, F_DUPFD_CLOEXEC, arg); > + case F_SETFL: { > + int arg = GET_ARG_MACRO(int); > + arg |= O_LARGEFILE; > + return syscall(SYS_fcntl, fd, cmd, arg); > + } The brace placement for case blocks like this isn't really idiomatic in musl. It's not horrible either, but might be nicer to declare the variables at the top. > + case F_DUPFD_CLOEXEC: { > + int ret; > + int arg = GET_ARG_MACRO(int); > + ret = __syscall(SYS_fcntl, fd, F_DUPFD_CLOEXEC, arg); > if (ret != -EINVAL) { > if (ret >= 0) > __syscall(SYS_fcntl, ret, F_SETFD, FD_CLOEXEC); > @@ -37,13 +53,37 @@ int fcntl(int fd, int cmd, ...) > if (ret >= 0) __syscall(SYS_fcntl, ret, F_SETFD, FD_CLOEXEC); > return __syscall_ret(ret); > } > - switch (cmd) { > - case F_SETLK: > + case F_DUPFD: > + case F_SETFD: > + case F_SETSIG: > + case F_SETLEASE: > + case F_ADD_SEALS: > + case F_SETOWN: > + case F_SETPIPE_SZ: > + case F_NOTIFY: { > + int arg = GET_ARG_MACRO(int); > + return syscall(SYS_fcntl, fd, cmd, arg); > + } > case F_GETLK: > + case F_SETLK: > + case F_OFD_SETLK: > + case F_OFD_GETLK: { > + struct flock * arg = GET_ARG_MACRO(struct flock *); > + return syscall(SYS_fcntl, fd, cmd, arg); > + } > + case F_OFD_SETLKW: > + case F_SETLKW: { > + struct flock * arg = GET_ARG_MACRO(struct flock *); > + return syscall_cp(SYS_fcntl, fd, cmd, arg); > + } > case F_GETOWN_EX: > - case F_SETOWN_EX: > - return syscall(SYS_fcntl, fd, cmd, (void *)arg); > - default: > + case F_SETOWN_EX: { > + struct f_owner_ex * arg = GET_ARG_MACRO(struct f_owner_ex *); > return syscall(SYS_fcntl, fd, cmd, arg); > } > + default: { > + unsigned long arg = GET_ARG_MACRO(unsigned long); > + return syscall(SYS_fcntl, fd, cmd, arg); > + } > + } > } > -- > 2.4.11 I'm attaching my old versions of the patch for reference, in case they suggest nicer ways to do anything. One is a patch that was already partially applied, I think (see above); the other is just a replacement .c file since the patch is not very readable. Rich View attachment "fcntl-arg-types.diff" of type "text/plain" (1800 bytes) View attachment "fcntl-type-correct-v2.c" of type "text/plain" (1699 bytes)
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.