diff options
author | Wolfgang Hommel <wolfcw@users.noreply.github.com> | 2021-03-06 09:46:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-06 09:46:07 +0100 |
commit | 8ae4c9bc0eaa80cd99cc3acdc001ee8bd8ac5800 (patch) | |
tree | 34e6f8927810de8ff521b6585c56dd44abeb33ef | |
parent | e1073c8733c4fdf1a182fc6a658a18d211965fbb (diff) | |
parent | 6733dc3a8d8c322f422e2570a47663a22385e25a (diff) | |
download | libfaketime-8ae4c9bc0eaa80cd99cc3acdc001ee8bd8ac5800.tar.gz |
Merge pull request #313 from dkg/test-variadic-promotion
Test variadic promotion
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/Makefile | 5 | ||||
-rw-r--r-- | src/faketime_common.h | 39 | ||||
-rw-r--r-- | src/libfaketime.c | 18 | ||||
-rw-r--r-- | test/Makefile | 21 | ||||
-rw-r--r-- | test/variadic/inner.c | 46 | ||||
-rw-r--r-- | test/variadic/main.c | 70 | ||||
-rw-r--r-- | test/variadic/outer.c | 21 |
8 files changed, 201 insertions, 20 deletions
@@ -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; +} |