summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>2021-02-24 12:36:38 -0500
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>2021-02-24 14:45:38 -0500
commit811283e683cd244ae14e77cd85a2c7d4266478eb (patch)
tree4d73f0ee3313e14035b5cd532d97acf323e51d6b /src
parenta8283c646d43518b7c8d9cd90ce8f627edd76a9e (diff)
downloadlibfaketime-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/Makefile4
-rw-r--r--src/libfaketime.c55
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
*