os-wrappers-test.c: Correctly forward arguments to fcntl

We can't just unconditionally read the optional arguments (and also read
it as a void* despite actually being an int).
While this happens to work on most architectures because the first few
variadic arguments are passed in registers, this is non-portable and
causes a crash on architectures that set bounds on variadic function
arguments (for example CHERI-enabled architectures). It could also cause
problems on big-endian architectures that pass variadic arguments on the
stack rather than in registers.

For CHERI-MIPS, reading sizeof(void*) causes a read of 16 bytes from the
bounded varargs capability. This always crashes since even calls with the
optional argument only have 4 bytes available.

Signed-off-by: Alex Richardson <Alexander.Richardson@cl.cam.ac.uk>
Reviewed-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
Alex Richardson 2021-03-18 10:45:50 +00:00 committed by Simon Ser
parent e4659ffbf5
commit e881934927

View file

@ -85,7 +85,8 @@ __attribute__ ((visibility("default"))) int
fcntl(int fd, int cmd, ...)
{
va_list ap;
void *arg;
int arg;
int has_arg;
wrapped_calls_fcntl++;
@ -93,12 +94,27 @@ fcntl(int fd, int cmd, ...)
errno = EINVAL;
return -1;
}
switch (cmd) {
case F_DUPFD_CLOEXEC:
case F_DUPFD:
case F_SETFD:
va_start(ap, cmd);
arg = va_arg(ap, int);
has_arg = 1;
va_end(ap);
break;
case F_GETFD:
has_arg = 0;
break;
default:
fprintf(stderr, "Unexpected fctnl cmd %d\n", cmd);
abort();
}
va_start(ap, cmd);
arg = va_arg(ap, void*);
va_end(ap);
return real_fcntl(fd, cmd, arg);
if (has_arg) {
return real_fcntl(fd, cmd, arg);
}
return real_fcntl(fd, cmd);
}
__attribute__ ((visibility("default"))) ssize_t