mirror of
https://github.com/wolfcw/libfaketime.git
synced 2026-05-17 00:26:16 +03:00
Fix cross-arch shared memory struct layout and ftruncate/munmap sizes
Replace struct timespec (arch-dependent long/time_t) with fixed-width int64_t pairs in ft_shared_s so that 32-bit and 64-bit processes interpret the same shared memory layout identically. Fix ftruncate calls that allocated sizeof(uint64_t) (8 bytes) instead of sizeof(struct ft_shared_s) (64-80 bytes) for the shared memory region. Fix munmap in ft_cleanup using the wrong size. Add struct layout test and cross-process shared memory functional test.
This commit is contained in:
@@ -290,7 +290,7 @@ int main (int argc, char **argv)
|
||||
}
|
||||
|
||||
/* set shm size */
|
||||
if (-1 == ftruncate(shm_fd, sizeof(uint64_t)))
|
||||
if (-1 == ftruncate(shm_fd, sizeof(struct ft_shared_s)))
|
||||
{
|
||||
perror("faketime: ftruncate");
|
||||
cleanup_shobjs();
|
||||
@@ -316,12 +316,16 @@ int main (int argc, char **argv)
|
||||
/* init elapsed time ticks to zero */
|
||||
ft_shared->ticks = 0;
|
||||
ft_shared->file_idx = 0;
|
||||
ft_shared->start_time.real.tv_sec = 0;
|
||||
ft_shared->start_time.real.tv_nsec = -1;
|
||||
ft_shared->start_time.mon.tv_sec = 0;
|
||||
ft_shared->start_time.mon.tv_nsec = -1;
|
||||
ft_shared->start_time.mon_raw.tv_sec = 0;
|
||||
ft_shared->start_time.mon_raw.tv_nsec = -1;
|
||||
ft_shared->start_time_real.sec = 0;
|
||||
ft_shared->start_time_real.nsec = -1;
|
||||
ft_shared->start_time_mon.sec = 0;
|
||||
ft_shared->start_time_mon.nsec = -1;
|
||||
ft_shared->start_time_mon_raw.sec = 0;
|
||||
ft_shared->start_time_mon_raw.nsec = -1;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
ft_shared->start_time_boot.sec = 0;
|
||||
ft_shared->start_time_boot.nsec = -1;
|
||||
#endif
|
||||
|
||||
if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s))))
|
||||
{
|
||||
|
||||
@@ -38,6 +38,13 @@ struct system_time_s
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Fixed-width time representation for shared memory (arch-independent) */
|
||||
struct ft_shared_time_s
|
||||
{
|
||||
int64_t sec;
|
||||
int64_t nsec;
|
||||
};
|
||||
|
||||
/* Data shared among faketime-spawned processes */
|
||||
struct ft_shared_s
|
||||
{
|
||||
@@ -47,8 +54,13 @@ struct ft_shared_s
|
||||
uint64_t ticks;
|
||||
/* Index of timestamp to be loaded from file */
|
||||
uint64_t file_idx;
|
||||
/* System time Faketime started at */
|
||||
struct system_time_s start_time;
|
||||
/* System time Faketime started at (fixed-width for cross-arch safety) */
|
||||
struct ft_shared_time_s start_time_real;
|
||||
struct ft_shared_time_s start_time_mon;
|
||||
struct ft_shared_time_s start_time_mon_raw;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
struct ft_shared_time_s start_time_boot;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* These are all needed in order to properly build on OSX */
|
||||
|
||||
@@ -588,6 +588,36 @@ int ft_sem_unlink(ft_sem_t *ft_sem)
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
static void system_time_to_shared(const struct system_time_s *src,
|
||||
struct ft_shared_s *dst)
|
||||
{
|
||||
dst->start_time_real.sec = (int64_t)src->real.tv_sec;
|
||||
dst->start_time_real.nsec = (int64_t)src->real.tv_nsec;
|
||||
dst->start_time_mon.sec = (int64_t)src->mon.tv_sec;
|
||||
dst->start_time_mon.nsec = (int64_t)src->mon.tv_nsec;
|
||||
dst->start_time_mon_raw.sec = (int64_t)src->mon_raw.tv_sec;
|
||||
dst->start_time_mon_raw.nsec = (int64_t)src->mon_raw.tv_nsec;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
dst->start_time_boot.sec = (int64_t)src->boot.tv_sec;
|
||||
dst->start_time_boot.nsec = (int64_t)src->boot.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void shared_to_system_time(const struct ft_shared_s *src,
|
||||
struct system_time_s *dst)
|
||||
{
|
||||
dst->real.tv_sec = (time_t)src->start_time_real.sec;
|
||||
dst->real.tv_nsec = (long)src->start_time_real.nsec;
|
||||
dst->mon.tv_sec = (time_t)src->start_time_mon.sec;
|
||||
dst->mon.tv_nsec = (long)src->start_time_mon.nsec;
|
||||
dst->mon_raw.tv_sec = (time_t)src->start_time_mon_raw.sec;
|
||||
dst->mon_raw.tv_nsec = (long)src->start_time_mon_raw.nsec;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
dst->boot.tv_sec = (time_t)src->start_time_boot.sec;
|
||||
dst->boot.tv_nsec = (long)src->start_time_boot.nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool shmCreator = false;
|
||||
|
||||
static void ft_shm_create(void) {
|
||||
@@ -617,7 +647,7 @@ static void ft_shm_create(void) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* set shm size */
|
||||
if (-1 == ftruncate(shm_fdN, sizeof(uint64_t)))
|
||||
if (-1 == ftruncate(shm_fdN, sizeof(struct ft_shared_s)))
|
||||
{
|
||||
perror("libfaketime: In ft_shm_create(), ftruncate failed");
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -637,12 +667,16 @@ static void ft_shm_create(void) {
|
||||
/* init elapsed time ticks to zero */
|
||||
ft_sharedN->ticks = 0;
|
||||
ft_sharedN->file_idx = 0;
|
||||
ft_sharedN->start_time.real.tv_sec = 0;
|
||||
ft_sharedN->start_time.real.tv_nsec = -1;
|
||||
ft_sharedN->start_time.mon.tv_sec = 0;
|
||||
ft_sharedN->start_time.mon.tv_nsec = -1;
|
||||
ft_sharedN->start_time.mon_raw.tv_sec = 0;
|
||||
ft_sharedN->start_time.mon_raw.tv_nsec = -1;
|
||||
ft_sharedN->start_time_real.sec = 0;
|
||||
ft_sharedN->start_time_real.nsec = -1;
|
||||
ft_sharedN->start_time_mon.sec = 0;
|
||||
ft_sharedN->start_time_mon.nsec = -1;
|
||||
ft_sharedN->start_time_mon_raw.sec = 0;
|
||||
ft_sharedN->start_time_mon_raw.nsec = -1;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
ft_sharedN->start_time_boot.sec = 0;
|
||||
ft_sharedN->start_time_boot.nsec = -1;
|
||||
#endif
|
||||
|
||||
if (-1 == munmap(ft_sharedN, (sizeof(struct ft_shared_s))))
|
||||
{
|
||||
@@ -864,7 +898,7 @@ static void ft_cleanup (void)
|
||||
/* detach from shared memory */
|
||||
if (ft_shared != NULL)
|
||||
{
|
||||
munmap(ft_shared, sizeof(uint64_t));
|
||||
munmap(ft_shared, sizeof(struct ft_shared_s));
|
||||
}
|
||||
if (stss != NULL)
|
||||
{
|
||||
@@ -989,7 +1023,7 @@ static void reset_time()
|
||||
perror("libfaketime: In reset_time(), ft_sem_lock failed");
|
||||
exit(1);
|
||||
}
|
||||
ft_shared->start_time = ftpl_starttime;
|
||||
system_time_to_shared(&ftpl_starttime, ft_shared);
|
||||
if (ft_sem_unlock(&shared_sem) == -1)
|
||||
{
|
||||
perror("libfaketime: In reset_time(), ft_sem_unlock failed");
|
||||
@@ -1087,11 +1121,11 @@ static bool load_time(struct timespec *tp)
|
||||
/* we set shared memory to stop using infile */
|
||||
ft_shared->ticks = 1;
|
||||
system_time_from_system(&ftpl_starttime);
|
||||
ft_shared->start_time = ftpl_starttime;
|
||||
system_time_to_shared(&ftpl_starttime, ft_shared);
|
||||
}
|
||||
else
|
||||
{
|
||||
ftpl_starttime = ft_shared->start_time;
|
||||
shared_to_system_time(ft_shared, &ftpl_starttime);
|
||||
}
|
||||
|
||||
munmap(stss, infile_size);
|
||||
@@ -3269,16 +3303,16 @@ static void ftpl_really_init(void)
|
||||
perror("libfaketime: In ftpl_init(), ft_sem_lock failed");
|
||||
exit(1);
|
||||
}
|
||||
if (ft_shared->start_time.real.tv_nsec == -1)
|
||||
if (ft_shared->start_time_real.nsec == -1)
|
||||
{
|
||||
/* set up global start time */
|
||||
system_time_from_system(&ftpl_starttime);
|
||||
ft_shared->start_time = ftpl_starttime;
|
||||
system_time_to_shared(&ftpl_starttime, ft_shared);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** get preset start time */
|
||||
ftpl_starttime = ft_shared->start_time;
|
||||
shared_to_system_time(ft_shared, &ftpl_starttime);
|
||||
}
|
||||
if (ft_sem_unlock(&shared_sem) == -1)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ OBJ = ${SRC:.c=.o}
|
||||
TEST_SNIPPETS = $(notdir $(basename $(wildcard snippets/*.c)))
|
||||
EXPECTATIONS = $(notdir $(basename $(wildcard snippets/*.variable)))
|
||||
|
||||
ALL_TESTS = timetest test
|
||||
ALL_TESTS = timetest test shm_layout_test
|
||||
|
||||
ifneq ($(filter -DINTERCEPT_SYSCALL,${CFLAGS}),)
|
||||
ALL_TESTS += confirm_variadic_promotion
|
||||
@@ -26,6 +26,9 @@ all: $(ALL_TESTS)
|
||||
timetest: ${OBJ}
|
||||
${CC} -o $@ ${OBJ} ${LDFLAGS}
|
||||
|
||||
shm_layout_test: shm_layout_test.c ../src/faketime_common.h
|
||||
${CC} -o $@ ${CFLAGS} $<
|
||||
|
||||
test: timetest functest libmallocintercept.so
|
||||
@echo
|
||||
@./test.sh
|
||||
@@ -76,7 +79,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}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so
|
||||
@rm -f ${OBJ} timetest shm_layout_test getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so
|
||||
|
||||
distclean: clean
|
||||
@echo
|
||||
|
||||
31
test/functests/test_shm_across_processes.sh
Normal file
31
test/functests/test_shm_across_processes.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
# Verify that shared memory works end-to-end across processes.
|
||||
# The faketime wrapper creates SHM with a known FAKETIME value,
|
||||
# and the child process (via LD_PRELOAD) reads it and reports
|
||||
# the faked time.
|
||||
|
||||
init()
|
||||
{
|
||||
typeset testsuite="$1"
|
||||
PLATFORM=$(platform)
|
||||
if [ -z "$PLATFORM" ]; then
|
||||
echo "$testsuite: unknown platform! quitting"
|
||||
return 1
|
||||
fi
|
||||
echo "# PLATFORM=$PLATFORM"
|
||||
return 0
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
init
|
||||
|
||||
run_testcase shm_year_check
|
||||
}
|
||||
|
||||
shm_year_check()
|
||||
{
|
||||
typeset expected="2020"
|
||||
typeset actual
|
||||
actual=$(fakecmd "2020-06-15 12:00:00" date -u +%Y)
|
||||
asserteq "$actual" "$expected" "child process should see faked year via SHM"
|
||||
}
|
||||
65
test/shm_layout_test.c
Normal file
65
test/shm_layout_test.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../src/faketime_common.h"
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check_offset(const char *field, size_t actual, size_t expected)
|
||||
{
|
||||
if (actual != expected)
|
||||
{
|
||||
fprintf(stderr, "FAIL: offsetof(ft_shared_s, %s) = %zu, expected %zu\n",
|
||||
field, actual, expected);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OK: offsetof(ft_shared_s, %s) = %zu\n", field, actual);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_size(const char *name, size_t actual, size_t expected)
|
||||
{
|
||||
if (actual != expected)
|
||||
{
|
||||
fprintf(stderr, "FAIL: sizeof(%s) = %zu, expected %zu\n",
|
||||
name, actual, expected);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OK: sizeof(%s) = %zu\n", name, actual);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
/* ft_shared_time_s must be exactly 16 bytes: two int64_t */
|
||||
check_size("ft_shared_time_s", sizeof(struct ft_shared_time_s), 16);
|
||||
|
||||
/* Field offsets in ft_shared_s */
|
||||
check_offset("ticks", offsetof(struct ft_shared_s, ticks), 0);
|
||||
check_offset("file_idx", offsetof(struct ft_shared_s, file_idx), 8);
|
||||
check_offset("start_time_real", offsetof(struct ft_shared_s, start_time_real), 16);
|
||||
check_offset("start_time_mon", offsetof(struct ft_shared_s, start_time_mon), 32);
|
||||
check_offset("start_time_mon_raw", offsetof(struct ft_shared_s, start_time_mon_raw), 48);
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
check_offset("start_time_boot", offsetof(struct ft_shared_s, start_time_boot), 64);
|
||||
check_size("ft_shared_s", sizeof(struct ft_shared_s), 80);
|
||||
#else
|
||||
check_size("ft_shared_s", sizeof(struct ft_shared_s), 64);
|
||||
#endif
|
||||
|
||||
if (failures > 0)
|
||||
{
|
||||
fprintf(stderr, "%d layout check(s) failed\n", failures);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("All layout checks passed\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user