From 71b31e908d2771cd4640ef1867a9b8bfc06645bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Mon, 23 Jun 2025 16:17:54 +0200 Subject: [PATCH 1/3] Add missing newline to error message --- src/libfaketime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfaketime.c b/src/libfaketime.c index 94bd74f..1fb5d37 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -504,7 +504,7 @@ static void ft_shm_create(void) { sscanf(shared_objsN, "%255s %255s", sem_nameT, shm_nameT); if (SEM_FAILED == (shared_semT = sem_open(sem_nameT, 0))) { - fprintf(stderr, "libfaketime: In ft_shm_create(), non-fatal sem_open issue with %s", sem_nameT); + fprintf(stderr, "libfaketime: In ft_shm_create(), non-fatal sem_open issue with %s\n", sem_nameT); } else { semSafetyCheckPassed = 1; From 2649cdb15694b45bbc544fd1608138a77f496f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Mon, 2 Jun 2025 12:49:28 +0200 Subject: [PATCH 2/3] Add semaphore abstraction layer Add ft_sem_*() functions that use the POSIX semaphore API. In preparation for adding System V semaphores as an alternative to POSIX semaphores, because glibc breaks POSIX semaphores when operating in mixed 32- and 64-bit environments[1]. [1] https://sourceware.org/bugzilla/show_bug.cgi?id=17980 --- src/libfaketime.c | 160 +++++++++++++++++++++++++++++++--------------- 1 file changed, 108 insertions(+), 52 deletions(-) diff --git a/src/libfaketime.c b/src/libfaketime.c index 1fb5d37..a7b06e4 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -334,8 +334,14 @@ int read_config_file(); bool str_array_contains(const char *haystack, const char *needle); void *ft_dlvsym(void *handle, const char *symbol, const char *version, const char *full_name, char *ignore_list, bool should_debug_dlsym); +typedef struct { + char *name; + sem_t *sem; +} ft_sem_t; + /** Semaphore protecting shared data */ -static sem_t *shared_sem = NULL; +static ft_sem_t shared_sem; +static bool shared_sem_initialized = false; /** Data shared among faketime-spawned processes */ static struct ft_shared_s *ft_shared = NULL; @@ -422,6 +428,51 @@ static bool parse_config_file = true; static void ft_cleanup (void) __attribute__ ((destructor)); static void ftpl_init (void) __attribute__ ((constructor)); +/* + * ======================================================================= + * Semaphore related functions === SEM + * ======================================================================= + */ + +int ft_sem_create(char *name, ft_sem_t *ft_sem) +{ + if (SEM_FAILED == (ft_sem->sem = sem_open(name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1))) + { + return -1; + } + ft_sem->name = name; + return 0; +} + +int ft_sem_open(char *name, ft_sem_t *ft_sem) +{ + if (SEM_FAILED == (ft_sem->sem = sem_open(name, 0))) + { + return -1; + } + ft_sem->name = name; + return 0; +} + +int ft_sem_lock(ft_sem_t *ft_sem) +{ + return sem_wait(ft_sem->sem); +} + +int ft_sem_unlock(ft_sem_t *ft_sem) +{ + return sem_post(ft_sem->sem); +} + +int ft_sem_close(ft_sem_t *ft_sem) +{ + return sem_close(ft_sem->sem); +} + +int ft_sem_unlink(ft_sem_t *ft_sem) +{ + return sem_unlink(ft_sem->name); +} /* * ======================================================================= @@ -434,10 +485,10 @@ static bool shmCreator = false; static void ft_shm_create(void) { char sem_name[256], shm_name[256], sem_nameT[256], shm_nameT[256]; int shm_fdN; - sem_t *semN; + ft_sem_t semN; struct ft_shared_s *ft_sharedN; char shared_objsN[513]; - sem_t *shared_semT = NULL; + ft_sem_t shared_semT; pid_t pid; #ifdef FAKE_PID @@ -447,8 +498,8 @@ static void ft_shm_create(void) { #endif snprintf(sem_name, 255, "/faketime_sem_%ld", (long)pid); snprintf(shm_name, 255, "/faketime_shm_%ld", (long)pid); - if (SEM_FAILED == (semN = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1))) - { /* silently fail on platforms that do not support sem_open() */ + if (-1 == ft_sem_create(sem_name, &semN)) + { /* silently fail on platforms that do not support semaphores */ return; } /* create shm */ @@ -470,9 +521,9 @@ static void ft_shm_create(void) { perror("libfaketime: In ft_shm_create(), mmap failed"); exit(EXIT_FAILURE); } - if (sem_wait(semN) == -1) + if (ft_sem_lock(&semN) == -1) { - perror("libfaketime: In ft_shm_create(), sem_wait failed"); + perror("libfaketime: In ft_shm_create(), ft_sem_lock failed"); exit(EXIT_FAILURE); } /* init elapsed time ticks to zero */ @@ -490,25 +541,25 @@ static void ft_shm_create(void) { perror("libfaketime: In ft_shm_create(), munmap failed"); exit(EXIT_FAILURE); } - if (sem_post(semN) == -1) + if (ft_sem_unlock(&semN) == -1) { - perror("libfaketime: In ft_shm_create(), sem_post failed"); + perror("libfaketime: In ft_shm_create(), ft_sem_unlock failed"); exit(EXIT_FAILURE); } snprintf(shared_objsN, sizeof(shared_objsN), "%s %s", sem_name, shm_name); int semSafetyCheckPassed = 0; - sem_close(semN); + ft_sem_close(&semN); sscanf(shared_objsN, "%255s %255s", sem_nameT, shm_nameT); - if (SEM_FAILED == (shared_semT = sem_open(sem_nameT, 0))) + if (-1 == ft_sem_open(sem_nameT, &shared_semT)) { - fprintf(stderr, "libfaketime: In ft_shm_create(), non-fatal sem_open issue with %s\n", sem_nameT); + fprintf(stderr, "libfaketime: In ft_shm_create(), non-fatal ft_sem_open issue with %s\n", sem_nameT); } else { semSafetyCheckPassed = 1; - sem_close(shared_semT); + ft_sem_close(&shared_semT); } if (semSafetyCheckPassed == 1) { @@ -520,6 +571,7 @@ static void ft_shm_create(void) { static void ft_shm_destroy(void) { char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED"); + ft_sem_t ft_sem; if (ft_shared_env != NULL) { @@ -536,7 +588,10 @@ static void ft_shm_destroy(void) Since there is no easy solution for this problem (see issue #56), ft_shm_init() below at least tries to handle this carefully. */ - sem_unlink(sem_name); + if (0 == ft_sem_open(sem_name, &ft_sem)) + { + ft_sem_unlink(&ft_sem); + } shm_unlink(shm_name); unsetenv("FAKETIME_SHARED"); } @@ -554,7 +609,7 @@ static void ft_shm_really_init (void) { int ticks_shm_fd; char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED"); - sem_t *shared_semR = NULL; + ft_sem_t shared_semR; static int nt=1; /* create semaphore and shared memory locally unless it has been passed along */ @@ -572,14 +627,14 @@ static void ft_shm_really_init (void) printf("libfaketime: In ft_shm_init(), error parsing semaphore name and shared memory id from string: %s", ft_shared_env); exit(1); } - if (SEM_FAILED == (shared_semR = sem_open(sem_name, 0))) /* gone stale? */ + if (-1 == ft_sem_open(sem_name, &shared_semR)) /* gone stale? */ { ft_shm_create(); ft_shared_env = getenv("FAKETIME_SHARED"); } else { - sem_close(shared_semR); + ft_sem_close(&shared_semR); } } @@ -592,7 +647,7 @@ static void ft_shm_really_init (void) exit(1); } - if (SEM_FAILED == (shared_sem = sem_open(sem_name, 0))) + if (-1 == ft_sem_open(sem_name, &shared_sem)) { if (shmCreator) { @@ -617,6 +672,7 @@ static void ft_shm_really_init (void) } } + shared_sem_initialized = true; if (-1 == (ticks_shm_fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR))) { @@ -648,10 +704,10 @@ static void ft_cleanup (void) { munmap(stss, infile_size); } - if (shared_sem != NULL) + if (shared_sem_initialized) { - sem_close(shared_sem); - shared_sem = NULL; + ft_sem_close(&shared_sem); + shared_sem_initialized = false; } #ifdef FAKE_PTHREAD if (pthread_rwlock_destroy(&monotonic_conds_lock) != 0) { @@ -727,11 +783,11 @@ static void system_time_from_system (struct system_time_s * systime) static void next_time(struct timespec *tp, struct timespec *ticklen) { - if (shared_sem != NULL) + if (shared_sem_initialized) { struct timespec inc; /* lock */ - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -740,7 +796,7 @@ static void next_time(struct timespec *tp, struct timespec *ticklen) } else { - perror("libfaketime: In next_time(), sem_wait failed"); + perror("libfaketime: In next_time(), ft_sem_lock failed"); exit(1); } } @@ -749,9 +805,9 @@ static void next_time(struct timespec *tp, struct timespec *ticklen) timespecadd(&user_faked_time_timespec, &inc, tp); (ft_shared->ticks)++; /* unlock */ - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In next_time(), sem_post failed"); + perror("libfaketime: In next_time(), ft_sem_unlock failed"); exit(1); } } @@ -760,17 +816,17 @@ static void next_time(struct timespec *tp, struct timespec *ticklen) static void reset_time() { system_time_from_system(&ftpl_starttime); - if (shared_sem != NULL) + if (shared_sem_initialized) { - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { - perror("libfaketime: In reset_time(), sem_wait failed"); + perror("libfaketime: In reset_time(), ft_sem_lock failed"); exit(1); } ft_shared->start_time = ftpl_starttime; - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In reset_time(), sem_post failed"); + perror("libfaketime: In reset_time(), ft_sem_unlock failed"); exit(1); } } @@ -785,7 +841,7 @@ static void reset_time() static void save_time(struct timespec *tp) { - if ((shared_sem != NULL) && (outfile != -1)) + if (shared_sem_initialized && (outfile != -1)) { struct saved_timestamp time_write; ssize_t written; @@ -795,7 +851,7 @@ static void save_time(struct timespec *tp) time_write.nsec = htobe64(tp->tv_nsec); /* lock */ - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -804,7 +860,7 @@ static void save_time(struct timespec *tp) } else { - perror("libfaketime: In save_time(), sem_wait failed"); + perror("libfaketime: In save_time(), ft_sem_lock failed"); exit(1); } } @@ -823,9 +879,9 @@ static void save_time(struct timespec *tp) } /* unlock */ - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In save_time(), sem_post failed"); + perror("libfaketime: In save_time(), ft_sem_unlcok failed"); exit(1); } } @@ -838,10 +894,10 @@ static void save_time(struct timespec *tp) static bool load_time(struct timespec *tp) { bool ret = false; - if ((shared_sem != NULL) && (infile_set)) + if (shared_sem_initialized && (infile_set)) { /* lock */ - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -849,7 +905,7 @@ static bool load_time(struct timespec *tp) } else { - perror("libfaketime: In load_time(), sem_wait failed"); + perror("libfaketime: In load_time(), ft_sem_lock failed"); exit(1); } } @@ -883,9 +939,9 @@ static bool load_time(struct timespec *tp) } /* unlock */ - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In load_time(), sem_post failed"); + perror("libfaketime: In load_time(), ft_sem_unlock failed"); exit(1); } } @@ -919,9 +975,9 @@ static bool user_per_tick_inc_set_backup = false; void lock_for_stat() { - if (shared_sem != NULL) + if (shared_sem_initialized) { - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -930,7 +986,7 @@ void lock_for_stat() } else { - perror("libfaketime: In lock_for_stat(), sem_wait failed"); + perror("libfaketime: In lock_for_stat(), ft_sem_lock failed"); exit(1); } } @@ -944,11 +1000,11 @@ void unlock_for_stat() { user_per_tick_inc_set = user_per_tick_inc_set_backup; - if (shared_sem != NULL) + if (shared_sem_initialized) { - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In unlock_for_stat(), sem_post failed"); + perror("libfaketime: In unlock_for_stat(), ft_sem_unlock failed"); exit(1); } } @@ -3032,11 +3088,11 @@ static void ftpl_really_init(void) user_faked_time_fmt[BUFSIZ - 1] = 0; } - if (shared_sem != 0) + if (shared_sem_initialized) { - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { - perror("libfaketime: In ftpl_init(), sem_wait failed"); + perror("libfaketime: In ftpl_init(), ft_sem_lock failed"); exit(1); } if (ft_shared->start_time.real.tv_nsec == -1) @@ -3050,9 +3106,9 @@ static void ftpl_really_init(void) /** get preset start time */ ftpl_starttime = ft_shared->start_time; } - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In ftpl_init(), sem_post failed"); + perror("libfaketime: In ftpl_init(), ft_sem_unlock failed"); exit(1); } } From 44c578c6d6eccfe7fe9a667cad6087932361cc84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Mon, 2 Jun 2025 17:02:01 +0200 Subject: [PATCH 3/3] Add optional System V semaphore backend By building with -DFT_SEMAPHORE_BACKEND=FT_SYSV, the System V semaphore API is unsed instead of POSIX. This works around a glibc bug[1] and fixes https://github.com/wolfcw/libfaketime/issues/427 ("libfaketime hangs forever when 32-bit process is executed within 64-bit process"). The default backend is still POSIX, in case there are regressions in the new code. [1] https://sourceware.org/bugzilla/show_bug.cgi?id=17980 Ref https://github.com/wolfcw/libfaketime/issues/427 --- src/libfaketime.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/libfaketime.c b/src/libfaketime.c index a7b06e4..accab96 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,20 @@ struct utimbuf { #endif #endif +/* semaphore backend options */ +#define FT_POSIX 1 +#define FT_SYSV 2 +/* set default backend */ +#ifndef FT_SEMAPHORE_BACKEND +#define FT_SEMAPHORE_BACKEND FT_POSIX +#endif +/* validate selected backend */ +#if FT_SEMAPHORE_BACKEND == FT_POSIX +#elif FT_SEMAPHORE_BACKEND == FT_SYSV +#else +#error "Unknown FT_SEMAPHORE_BACKEND; select between FT_POSIX and FT_SYSV" +#endif + #ifdef FAKE_RANDOM #include #endif @@ -334,9 +349,14 @@ int read_config_file(); bool str_array_contains(const char *haystack, const char *needle); void *ft_dlvsym(void *handle, const char *symbol, const char *version, const char *full_name, char *ignore_list, bool should_debug_dlsym); + typedef struct { char *name; +#if FT_SEMAPHORE_BACKEND == FT_POSIX sem_t *sem; +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + int semid; +#endif } ft_sem_t; /** Semaphore protecting shared data */ @@ -434,44 +454,128 @@ static void ftpl_init (void) __attribute__ ((constructor)); * ======================================================================= */ +#if FT_SEMAPHORE_BACKEND == FT_SYSV +int ft_sem_name2key(char *name) +{ + key_t key; + char fullname[256]; + snprintf(fullname, sizeof(fullname), "/tmp%s", name); + fullname[sizeof(fullname) - 1] = '\0'; + int fd = open(fullname, O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) + { + perror("libfaketime: open"); + return -1; + } + close(fd); + if (-1 == (key = ftok(fullname, 'F'))) + { + perror("libfaketime: ftok"); + return -1; + } + return key; +} +#endif + int ft_sem_create(char *name, ft_sem_t *ft_sem) { +#if FT_SEMAPHORE_BACKEND == FT_POSIX if (SEM_FAILED == (ft_sem->sem = sem_open(name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1))) { return -1; } +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + key_t key = ft_sem_name2key(name); + int nsems = 1; + + ft_sem->semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | S_IWUSR | S_IRUSR); + if (ft_sem->semid >= 0) { /* we got here first */ + struct sembuf sb = { + .sem_num = 0, + .sem_op = 1, /* number of resources the semaphore has (when decremented down to 0 the semaphore will block) */ + .sem_flg = 0, + }; + /* the number of semaphore operation structures (struct sembuf) passed to semop */ + int nsops = 1; + + /* do a semop() to "unlock" the semaphore */ + if (-1 == semop(ft_sem->semid, &sb, nsops)) { + return -1; + } + } else if (errno == EEXIST) { /* someone else got here before us */ + return -1; + } else { + return -1; + } +#endif ft_sem->name = name; return 0; } int ft_sem_open(char *name, ft_sem_t *ft_sem) { +#if FT_SEMAPHORE_BACKEND == FT_POSIX if (SEM_FAILED == (ft_sem->sem = sem_open(name, 0))) { return -1; } +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + key_t key = ft_sem_name2key(name); + ft_sem->semid = semget(key, 1, S_IWUSR | S_IRUSR); + if (ft_sem->semid < 0) { + return -1; + } +#endif ft_sem->name = name; return 0; } int ft_sem_lock(ft_sem_t *ft_sem) { +#if FT_SEMAPHORE_BACKEND == FT_POSIX return sem_wait(ft_sem->sem); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + struct sembuf sb = { + .sem_num = 0, + .sem_op = -1, /* allocate resource (lock) */ + .sem_flg = 0, + }; + return semop(ft_sem->semid, &sb, 1); +#endif } int ft_sem_unlock(ft_sem_t *ft_sem) { +#if FT_SEMAPHORE_BACKEND == FT_POSIX return sem_post(ft_sem->sem); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + struct sembuf sb = { + .sem_num = 0, + .sem_op = 1, /* free resource (unlock) */ + .sem_flg = 0, + }; + return semop(ft_sem->semid, &sb, 1); +#endif } int ft_sem_close(ft_sem_t *ft_sem) { +#if FT_SEMAPHORE_BACKEND == FT_POSIX return sem_close(ft_sem->sem); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + /* NOP -- there's no "close" equivalent */ + (void)ft_sem; + return 0; +#endif } int ft_sem_unlink(ft_sem_t *ft_sem) { +#if FT_SEMAPHORE_BACKEND == FT_POSIX return sem_unlink(ft_sem->name); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + return semctl(ft_sem->semid, 0, IPC_RMID); +#endif } /*