diff options
author | Daniel Kahn Gillmor <dkg@fifthhorseman.net> | 2021-02-24 12:36:38 -0500 |
---|---|---|
committer | Daniel Kahn Gillmor <dkg@fifthhorseman.net> | 2021-02-24 14:45:38 -0500 |
commit | 811283e683cd244ae14e77cd85a2c7d4266478eb (patch) | |
tree | 4d73f0ee3313e14035b5cd532d97acf323e51d6b /src | |
parent | a8283c646d43518b7c8d9cd90ce8f627edd76a9e (diff) | |
download | libfaketime-811283e683cd244ae14e77cd85a2c7d4266478eb.tar.gz |
Intercept syscall
This is an attempt at an implementation to address #301.
Some things worth noting:
- I am not particularly confident in my reverse of the variadic C
ABI. While the code appears to work for me on x86_64, I could
imagine some variations between platforms that I'm not
understanding.
- This works to intercept the invocation of syscall as seen in
test/syscalltest.sh, as long as it was compiled with -DFAKE_RANDOM
- defining -DINTERCEPT_SYSCALL on non-Linux platforms should result
in a compile-time error.
- This does *not* work to intercept the syscall sent by `openssl
rand`, for some reason I don't yet understand. Perhaps openssl has
some platform-specific syscall mechanism that doesn't route them
through libc's syscall() shim?
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 4 | ||||
-rw-r--r-- | src/libfaketime.c | 55 |
2 files changed, 59 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile index e8c7fac..95f711e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -44,6 +44,10 @@ # FAKE_PID # - Intercept getpid() # +# INTERCEPT_SYSCALL +# - (On GNU/Linux only) intercept glibc's syscall() for known relevant syscalls. +# If enabled, this currently only works to divert the getrandom syscall. +# # FORCE_MONOTONIC_FIX # - If the test program hangs forever on # " pthread_cond_timedwait: CLOCK_MONOTONIC test diff --git a/src/libfaketime.c b/src/libfaketime.c index f86a207..856f5a5 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -43,6 +43,14 @@ #include <sys/types.h> #include <netinet/in.h> #include <limits.h> +#ifdef INTERCEPT_SYSCALL +#ifdef __linux__ +#include <stdarg.h> +#include <sys/syscall.h> +#else +#error INTERCEPT_SYSCALL should only be defined on GNU/Linux systems. +#endif +#endif #include "uthash.h" @@ -230,6 +238,10 @@ static ssize_t (*real_getrandom) (void *buf, size_t buflen, unsigned static pid_t (*real_getpid) (); #endif +#ifdef INTERCEPT_SYSCALL +static long (*real_syscall) (long, ...); +#endif + static int initialized = 0; /* prototypes */ @@ -2460,6 +2472,10 @@ static void ftpl_init(void) real_getpid = dlsym(RTLD_NEXT, "getpid"); #endif +#ifdef INTERCEPT_SYSCALL + real_syscall = dlsym(RTLD_NEXT, "syscall"); +#endif + #ifdef FAKE_PTHREAD #ifdef __GLIBC__ @@ -3716,6 +3732,45 @@ pid_t getpid() { } #endif +#ifdef INTERCEPT_SYSCALL +/* see https://github.com/wolfcw/libfaketime/issues/301 */ +long syscall(long number, ...) { + va_list ap; + va_start(ap, number); +#ifdef FAKE_RANDOM + if (number == __NR_getrandom && getenv("FAKERANDOM_SEED")) { + void *buf; + size_t buflen; + unsigned int flags; + buf = va_arg(ap, void*); + buflen = va_arg(ap, size_t); + flags = va_arg(ap, unsigned int); + va_end(ap); + return getrandom(buf, buflen, flags); + } +#endif +/* + Invocations of C variadic arguments that are smaller than int are + promoted to int. For larger arguments, it's likely that they are + chopped into int-sized pieces. + + So the passthrough part of this code is attempting to reverse that + ABI so we can pass the arguments back into syscall(). + + Note that the Linux kernel appears to have baked-in 6 as the + maximum number of arguments for a syscall beyond the syscall number + itself. +*/ +#define vararg_promotion_t int +#define syscall_max_args 6 + vararg_promotion_t a[syscall_max_args]; + for (int i = 0; i < syscall_max_args; i++) + a[i] = va_arg(ap, vararg_promotion_t); + va_end(ap); + return real_syscall(number, a[0], a[1], a[2], a[3], a[4], a[5]); +} +#endif + /* * Editor modelines * |