summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWolfgang Hommel <wolfcw@users.noreply.github.com>2021-03-06 09:46:07 +0100
committerGitHub <noreply@github.com>2021-03-06 09:46:07 +0100
commit8ae4c9bc0eaa80cd99cc3acdc001ee8bd8ac5800 (patch)
tree34e6f8927810de8ff521b6585c56dd44abeb33ef
parente1073c8733c4fdf1a182fc6a658a18d211965fbb (diff)
parent6733dc3a8d8c322f422e2570a47663a22385e25a (diff)
downloadlibfaketime-8ae4c9bc0eaa80cd99cc3acdc001ee8bd8ac5800.tar.gz
Merge pull request #313 from dkg/test-variadic-promotion
Test variadic promotion
-rw-r--r--.gitignore1
-rw-r--r--src/Makefile5
-rw-r--r--src/faketime_common.h39
-rw-r--r--src/libfaketime.c18
-rw-r--r--test/Makefile21
-rw-r--r--test/variadic/inner.c46
-rw-r--r--test/variadic/main.c70
-rw-r--r--test/variadic/outer.c21
8 files changed, 201 insertions, 20 deletions
diff --git a/.gitignore b/.gitignore
index 0a10c6a..6dfed37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ test/run_*
test/repeat_random
test/getentropy_test
test/syscall_test
+test/variadic_promotion
src/libfaketime.dylib.1
src/libfaketime.1.dylib
diff --git a/src/Makefile b/src/Makefile
index 95f711e..ad2b797 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -48,6 +48,11 @@
# - (On GNU/Linux only) intercept glibc's syscall() for known relevant syscalls.
# If enabled, this currently only works to divert the getrandom syscall.
#
+# - note that on unusual architectures, if INTERCEPT_SYSCALL is set, you may
+# need to explicitly define variadic_promotion_t (e.g. by putting
+# -Dvariadic_promotion_t=int into CFLAGS). See src/faketime_common.h for
+# more info.
+#
# FORCE_MONOTONIC_FIX
# - If the test program hangs forever on
# " pthread_cond_timedwait: CLOCK_MONOTONIC test
diff --git a/src/faketime_common.h b/src/faketime_common.h
index 9fda6a7..0ae5fd4 100644
--- a/src/faketime_common.h
+++ b/src/faketime_common.h
@@ -58,4 +58,43 @@ struct ft_shared_s
#include <mach/mach_port.h>
#endif
+/*
+ Variadic Argument Re-packing
+
+ Functions with variadic arguments typically have most arguments
+ passed on the stack, but it varies across ABIs.
+
+ C specifies that variadic arguments that are smaller than some
+ standard promotion size are promoted to "int or larger". If your
+ platform's ABI only promotes to "int" and not "long" (and "int" and
+ "long" differ on your platform), you should probably add
+ -Dvariadic_promotion_t=int to CFLAGS.
+
+ Note that some ABIs do not put all the variadic arguments on the
+ stack. For example, x86-64 puts float and double variadic
+ arguments into floating point registers, according to
+ https://www.uclibc.org/docs/psABI-x86_64.pdf
+
+ The only variadic function faketime cares about intercepting is
+ syscall. But we don't believe that any syscalls expect float or
+ double arguments, so we hope all the rest will be on the stack.
+ tests/variadic/ attempts to confirm this if you are compiling
+ with -DINTERCEPT_SYSCALL.
+
+ If libc were capable of exposing a variadic form of syscall, we
+ could depend on that and drop this approach, which would be
+ preferable: https://sourceware.org/bugzilla/show_bug.cgi?id=27508
+*/
+#ifndef variadic_promotion_t
+#define variadic_promotion_t long
+#endif
+
+/*
+ The Linux kernel appears to have baked-in 6 as the maximum number
+ of arguments for a syscall beyond the syscall number itself.
+*/
+#ifndef syscall_max_args
+#define syscall_max_args 6
+#endif
+
#endif
diff --git a/src/libfaketime.c b/src/libfaketime.c
index f1bc253..24e8695 100644
--- a/src/libfaketime.c
+++ b/src/libfaketime.c
@@ -3781,23 +3781,9 @@ long syscall(long number, ...) {
return clock_gettime(clk_id, tp);
}
-/*
- 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 long
-#define syscall_max_args 6
- vararg_promotion_t a[syscall_max_args];
+ variadic_promotion_t a[syscall_max_args];
for (int i = 0; i < syscall_max_args; i++)
- a[i] = va_arg(ap, vararg_promotion_t);
+ a[i] = va_arg(ap, variadic_promotion_t);
va_end(ap);
if (!initialized)
ftpl_init();
diff --git a/test/Makefile b/test/Makefile
index 1c4208d..1b2a4aa 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,7 +1,7 @@
CC = gcc
-CFLAGS = -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS)
-LDFLAGS = -lrt -lpthread
+CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS)
+LDFLAGS += -lrt -lpthread
SRC = timetest.c
OBJ = ${SRC:.c=.o}
@@ -9,7 +9,13 @@ OBJ = ${SRC:.c=.o}
TEST_SNIPPETS = $(notdir $(basename $(wildcard snippets/*.c)))
EXPECTATIONS= $(notdir $(basename $(wildcard snippets/*.variable)))
-all: timetest test
+ALL_TESTS = timetest test
+
+ifneq ($(filter -DINTERCEPT_SYSCALL,${CFLAGS}),)
+ALL_TESTS += confirm_variadic_promotion
+endif
+
+all: $(ALL_TESTS)
.c.o:
${CC} -c ${CFLAGS} $<
@@ -31,6 +37,13 @@ functest:
randomtest: repeat_random
./randomtest.sh
+# ensure our variadic argument unpacking/repacking works as expected
+confirm_variadic_promotion: variadic_promotion
+ ./variadic_promotion
+variadic_promotion: variadic/main.o variadic/outer.o variadic/inner.o
+ ${CC} -o $@ ${CFLAGS} $^
+variadic/%.o: variadic/%.c
+ ${CC} -c -o $@ ${CFLAGS} $<
# run snippet tests
snippets: test_variable_data test_library_constructors
@@ -57,7 +70,7 @@ use_lib_%: _use_lib_test.c snippets/%.c lib%.so
## cleanup and metainformation
clean:
- @rm -f ${OBJ} timetest getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f})
+ @rm -f ${OBJ} timetest getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o
distclean: clean
@echo
diff --git a/test/variadic/inner.c b/test/variadic/inner.c
new file mode 100644
index 0000000..cc4426e
--- /dev/null
+++ b/test/variadic/inner.c
@@ -0,0 +1,46 @@
+
+#include <stdio.h>
+#include <wchar.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+/* round 0: c, s, wc, i, wi */
+long inner0(char *out, ...) {
+ char c = 0;
+ short s = 0;
+ wchar_t wc = 0;
+ int i = 0;
+ wint_t wi = 0;
+
+ va_list ap;
+ va_start(ap, out);
+ c = va_arg(ap, int);
+ s = va_arg(ap, int);
+ wc = va_arg(ap, typeof(wc));
+ i = va_arg(ap, typeof(i));
+ wi = va_arg(ap, typeof(wi));
+ va_end(ap);
+
+ int ret = sprintf(out, "c: 0x%x s: 0x%x wc: 0x%lx i: 0x%x wi: 0x%x\n", c, s, (long)wc, i, wi);
+ return ret;
+}
+/* round 1: l, ll, ptr, pd, sz */
+long inner1(char *out, ...) {
+ long l = 0;
+ long long ll = 0;
+ void *ptr = NULL;
+ ptrdiff_t pd = 0;
+ size_t sz = 0;
+
+ va_list ap;
+ va_start(ap, out);
+ l = va_arg(ap, typeof(l));
+ ll = va_arg(ap, typeof(ll));
+ ptr = va_arg(ap, typeof(ptr));
+ pd = va_arg(ap, typeof(pd));
+ sz = va_arg(ap, typeof(sz));
+ va_end(ap);
+
+ int ret = sprintf(out, "l: 0x%lx ll: 0x%llx ptr: %p pd: 0x%tx sz: 0x%zx\n", l, ll, ptr, pd, sz);
+ return ret;
+}
diff --git a/test/variadic/main.c b/test/variadic/main.c
new file mode 100644
index 0000000..2ee276e
--- /dev/null
+++ b/test/variadic/main.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <wchar.h>
+
+extern long outer(long num, char *out, ...);
+extern long inner0(char *out, ...);
+extern long inner1(char *out, ...);
+
+#define bufsize 2048
+
+static int compare_buffers(int round,
+ long ret_outer, long ret_inner,
+ const char* outer, const char* inner) {
+ int ret = 0;
+ if (ret_outer != ret_inner) {
+ printf("Round %d: return values differ (outer: %ld inner: %ld)\n", round, ret_outer, ret_inner);
+ ret++;
+ }
+ if (memcmp(outer, inner, bufsize)) {
+ printf("Round %d strings differ:\n - outer: %s\n - inner: %s\n", round, outer, inner);
+ ret++;
+ }
+ if (ret == 0)
+ printf("Round %d success: %s\n", round, outer);
+ return ret;
+}
+
+int main() {
+/* sizes of intrinsic types as reported by echo | cpp -dM | grep
+ SIZEOF, pruned to avoid floating point types. Should work with
+ both clang and gcc, not sure about other C preprocessors.
+
+ Note that we set bits in every high octet and every low octet to
+ see that they end up in the right spot.
+ */
+ char c = 0x03L;
+ short s = (0x04L << ((__SIZEOF_SHORT__ - 1) * 8)) + 0xff;
+ wchar_t wc = (0x05L << ((__SIZEOF_WCHAR_T__ - 1) * 8)) + 0xfe;
+ int i = (0x06L << ((__SIZEOF_INT__ - 1) * 8)) + 0xfd;
+ wint_t wi = (0x07L << ((__SIZEOF_WINT_T__ - 1) * 8)) + 0xfc;
+ long l = (0x08L << ((__SIZEOF_LONG__ - 1) * 8) ) + 0xfb;
+ long long ll = (0x09LL << ((__SIZEOF_LONG_LONG__ - 1) * 8)) + 0xfa;
+ void *ptr = (void*)((0x0aL << ((__SIZEOF_POINTER__ - 1) * 8)) + 0xf9);
+ ptrdiff_t pd = (0x0bL << ((__SIZEOF_PTRDIFF_T__ -1) * 8)) + 0xf9;
+ size_t sz = (0x0cL << ((__SIZEOF_SIZE_T__ - 1) * 8)) + 0xf8;
+
+ char *buf[2];
+ for (int j = 0; j < 2; j++)
+ buf[j] = malloc(bufsize);
+
+ int ret[2];
+ int errors = 0;
+
+#define reset_buffers(n) for (int j = 0; j < 2; j++) memset(buf[j], n, bufsize)
+#define check_buffers(n) errors += compare_buffers(n, ret[0], ret[1], buf[0], buf[1])
+
+ reset_buffers(0);
+ ret[0] = outer(0, buf[0], c, s, wc, i, wi);
+ ret[1] = inner0(buf[1], c, s, wc, i, wi);
+ check_buffers(0);
+
+ reset_buffers(1);
+ ret[0] = outer(1, buf[0], l, ll, ptr, pd, sz);
+ ret[1] = inner1(buf[1], l, ll, ptr, pd, sz);
+ check_buffers(1);
+
+ return (int)errors;
+}
diff --git a/test/variadic/outer.c b/test/variadic/outer.c
new file mode 100644
index 0000000..08f9dfb
--- /dev/null
+++ b/test/variadic/outer.c
@@ -0,0 +1,21 @@
+#include <stdarg.h>
+#include <time.h>
+#include "../../src/faketime_common.h"
+
+extern long inner0(char *out, ...);
+extern long inner1(char *out, ...);
+
+long outer(long num, char *out, ...) {
+ va_list ap;
+ va_start(ap, out);
+ variadic_promotion_t a[syscall_max_args];
+ for (int i = 0; i < syscall_max_args; i++)
+ a[i] = va_arg(ap, variadic_promotion_t);
+ va_end(ap);
+
+ if (num == 0)
+ return inner0(out, a[0], a[1], a[2], a[3], a[4], a[5]);
+ if (num == 1)
+ return inner1(out, a[0], a[1], a[2], a[3], a[4], a[5]);
+ else return -1;
+}