Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
clock_nanosleep
exec
futex
gettime_perf
gettime_perf_cold
procfs
timens
timer
timerfd
vfork_exec
@@ -0,0 +1,7 @@
TEST_GEN_PROGS := timens timerfd timer clock_nanosleep procfs exec futex vfork_exec
TEST_GEN_PROGS_EXTENDED := gettime_perf
CFLAGS := -Wall -Werror -pthread
LDLIBS := -lrt -ldl
include ../lib.mk
@@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sched.h>
#include <sys/timerfd.h>
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include "log.h"
#include "timens.h"
void test_sig(int sig)
{
if (sig == SIGUSR2)
pthread_exit(NULL);
}
struct thread_args {
struct timespec *now, *rem;
pthread_mutex_t *lock;
int clockid;
int abs;
};
void *call_nanosleep(void *_args)
{
struct thread_args *args = _args;
clock_nanosleep(args->clockid, args->abs ? TIMER_ABSTIME : 0, args->now, args->rem);
pthread_mutex_unlock(args->lock);
return NULL;
}
static int run_test(int clockid, int abs)
{
struct timespec now = {}, rem;
struct thread_args args = { .now = &now, .rem = &rem, .clockid = clockid};
struct timespec start;
pthread_mutex_t lock;
pthread_t thread;
int j, ok, ret;
signal(SIGUSR1, test_sig);
signal(SIGUSR2, test_sig);
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock);
if (clock_gettime(clockid, &start) == -1) {
if (errno == EINVAL && check_skip(clockid))
return 0;
return pr_perror("clock_gettime");
}
if (abs) {
now.tv_sec = start.tv_sec;
now.tv_nsec = start.tv_nsec;
}
now.tv_sec += 3600;
args.abs = abs;
args.lock = &lock;
ret = pthread_create(&thread, NULL, call_nanosleep, &args);
if (ret != 0) {
pr_err("Unable to create a thread: %s", strerror(ret));
return 1;
}
/* Wait when the thread will call clock_nanosleep(). */
ok = 0;
for (j = 0; j < 8; j++) {
/* The maximum timeout is about 5 seconds. */
usleep(10000 << j);
/* Try to interrupt clock_nanosleep(). */
pthread_kill(thread, SIGUSR1);
usleep(10000 << j);
/* Check whether clock_nanosleep() has been interrupted or not. */
if (pthread_mutex_trylock(&lock) == 0) {
/**/
ok = 1;
break;
}
}
if (!ok)
pthread_kill(thread, SIGUSR2);
pthread_join(thread, NULL);
pthread_mutex_destroy(&lock);
if (!ok) {
ksft_test_result_pass("clockid: %d abs:%d timeout\n", clockid, abs);
return 1;
}
if (rem.tv_sec < 3300 || rem.tv_sec > 3900) {
pr_fail("clockid: %d abs: %d remain: %ld\n",
clockid, abs, rem.tv_sec);
return 1;
}
ksft_test_result_pass("clockid: %d abs:%d\n", clockid, abs);
return 0;
}
int main(int argc, char *argv[])
{
int ret, nsfd;
ksft_print_header();
nscheck();
ksft_set_plan(4);
check_supported_timers();
if (unshare_timens())
return 1;
if (_settime(CLOCK_MONOTONIC, 7 * 24 * 3600))
return 1;
if (_settime(CLOCK_BOOTTIME, 9 * 24 * 3600))
return 1;
nsfd = open("/proc/self/ns/time_for_children", O_RDONLY);
if (nsfd < 0)
return pr_perror("Unable to open timens_for_children");
if (setns(nsfd, CLONE_NEWTIME))
return pr_perror("Unable to set timens");
ret = 0;
ret |= run_test(CLOCK_MONOTONIC, 0);
ret |= run_test(CLOCK_MONOTONIC, 1);
ret |= run_test(CLOCK_BOOTTIME_ALARM, 0);
ret |= run_test(CLOCK_BOOTTIME_ALARM, 1);
if (ret)
ksft_exit_fail();
ksft_exit_pass();
return ret;
}
@@ -0,0 +1 @@
CONFIG_TIME_NS=y
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include "log.h"
#include "timens.h"
#define OFFSET (36000)
int main(int argc, char *argv[])
{
struct timespec now, tst;
int status, i;
pid_t pid;
if (argc > 1) {
if (sscanf(argv[1], "%ld", &now.tv_sec) != 1)
return pr_perror("sscanf");
for (i = 0; i < 2; i++) {
_gettime(CLOCK_MONOTONIC, &tst, i);
if (labs(tst.tv_sec - now.tv_sec) > 5)
return pr_fail("%ld %ld\n", now.tv_sec, tst.tv_sec);
}
return 0;
}
ksft_print_header();
nscheck();
ksft_set_plan(1);
clock_gettime(CLOCK_MONOTONIC, &now);
if (unshare_timens())
return 1;
if (_settime(CLOCK_MONOTONIC, OFFSET))
return 1;
for (i = 0; i < 2; i++) {
_gettime(CLOCK_MONOTONIC, &tst, i);
if (labs(tst.tv_sec - now.tv_sec) > 5)
return pr_fail("%ld %ld\n",
now.tv_sec, tst.tv_sec);
}
if (argc > 1)
return 0;
pid = fork();
if (pid < 0)
return pr_perror("fork");
if (pid == 0) {
char now_str[64];
char *cargv[] = {"exec", now_str, NULL};
char *cenv[] = {NULL};
/* Check that a child process is in the new timens. */
for (i = 0; i < 2; i++) {
_gettime(CLOCK_MONOTONIC, &tst, i);
if (labs(tst.tv_sec - now.tv_sec - OFFSET) > 5)
return pr_fail("%ld %ld\n",
now.tv_sec + OFFSET, tst.tv_sec);
}
/* Check for proper vvar offsets after execve. */
snprintf(now_str, sizeof(now_str), "%ld", now.tv_sec + OFFSET);
execve("/proc/self/exe", cargv, cenv);
return pr_perror("execve");
}
if (waitpid(pid, &status, 0) != pid)
return pr_perror("waitpid");
if (status)
ksft_exit_fail();
ksft_test_result_pass("exec\n");
ksft_exit_pass();
return 0;
}
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sched.h>
#include <linux/unistd.h>
#include <linux/futex.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include "log.h"
#include "timens.h"
#define NSEC_PER_SEC 1000000000ULL
static int run_test(int clockid)
{
int futex_op = FUTEX_WAIT_BITSET;
struct timespec timeout, end;
int val = 0;
if (clockid == CLOCK_REALTIME)
futex_op |= FUTEX_CLOCK_REALTIME;
clock_gettime(clockid, &timeout);
timeout.tv_nsec += NSEC_PER_SEC / 10; // 100ms
if (timeout.tv_nsec > NSEC_PER_SEC) {
timeout.tv_sec++;
timeout.tv_nsec -= NSEC_PER_SEC;
}
if (syscall(__NR_futex, &val, futex_op, 0,
&timeout, 0, FUTEX_BITSET_MATCH_ANY) >= 0) {
ksft_test_result_fail("futex didn't return ETIMEDOUT\n");
return 1;
}
if (errno != ETIMEDOUT) {
ksft_test_result_fail("futex didn't return ETIMEDOUT: %s\n",
strerror(errno));
return 1;
}
clock_gettime(clockid, &end);
if (end.tv_sec < timeout.tv_sec ||
(end.tv_sec == timeout.tv_sec && end.tv_nsec < timeout.tv_nsec)) {
ksft_test_result_fail("futex slept less than 100ms\n");
return 1;
}
ksft_test_result_pass("futex with the %d clockid\n", clockid);
return 0;
}
int main(int argc, char *argv[])
{
int status, len, fd;
char buf[4096];
pid_t pid;
struct timespec mtime_now;
ksft_print_header();
nscheck();
ksft_set_plan(2);
clock_gettime(CLOCK_MONOTONIC, &mtime_now);
if (unshare_timens())
return 1;
len = snprintf(buf, sizeof(buf), "%d %d 0",
CLOCK_MONOTONIC, 70 * 24 * 3600);
fd = open("/proc/self/timens_offsets", O_WRONLY);
if (fd < 0)
return pr_perror("/proc/self/timens_offsets");
if (write(fd, buf, len) != len)
return pr_perror("/proc/self/timens_offsets");
close(fd);
pid = fork();
if (pid < 0)
return pr_perror("Unable to fork");
if (pid == 0) {
int ret = 0;
ret |= run_test(CLOCK_REALTIME);
ret |= run_test(CLOCK_MONOTONIC);
if (ret)
ksft_exit_fail();
ksft_exit_pass();
return 0;
}
if (waitpid(pid, &status, 0) != pid)
return pr_perror("Unable to wait the child process");
if (WIFEXITED(status))
return WEXITSTATUS(status);
return 1;
}
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <dlfcn.h>
#include "log.h"
#include "timens.h"
typedef int (*vgettime_t)(clockid_t, struct timespec *);
vgettime_t vdso_clock_gettime;
static void fill_function_pointers(void)
{
void *vdso = dlopen("linux-vdso.so.1",
RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso)
vdso = dlopen("linux-gate.so.1",
RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso)
vdso = dlopen("linux-vdso32.so.1",
RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso)
vdso = dlopen("linux-vdso64.so.1",
RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso) {
pr_err("[WARN]\tfailed to find vDSO\n");
return;
}
vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime");
if (!vdso_clock_gettime)
vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__kernel_clock_gettime");
if (!vdso_clock_gettime)
pr_err("Warning: failed to find clock_gettime in vDSO\n");
}
static void test(clock_t clockid, char *clockstr, bool in_ns)
{
struct timespec tp, start;
long i = 0;
const int timeout = 3;
vdso_clock_gettime(clockid, &start);
tp = start;
for (tp = start; start.tv_sec + timeout > tp.tv_sec ||
(start.tv_sec + timeout == tp.tv_sec &&
start.tv_nsec > tp.tv_nsec); i++) {
vdso_clock_gettime(clockid, &tp);
}
ksft_test_result_pass("%s:\tclock: %10s\tcycles:\t%10ld\n",
in_ns ? "ns" : "host", clockstr, i);
}
int main(int argc, char *argv[])
{
time_t offset = 10;
int nsfd;
ksft_print_header();
ksft_set_plan(8);
fill_function_pointers();
test(CLOCK_MONOTONIC, "monotonic", false);
test(CLOCK_MONOTONIC_COARSE, "monotonic-coarse", false);
test(CLOCK_MONOTONIC_RAW, "monotonic-raw", false);
test(CLOCK_BOOTTIME, "boottime", false);
nscheck();
if (unshare_timens())
return 1;
nsfd = open("/proc/self/ns/time_for_children", O_RDONLY);
if (nsfd < 0)
return pr_perror("Can't open a time namespace");
if (_settime(CLOCK_MONOTONIC, offset))
return 1;
if (_settime(CLOCK_BOOTTIME, offset))
return 1;
if (setns(nsfd, CLONE_NEWTIME))
return pr_perror("setns");
test(CLOCK_MONOTONIC, "monotonic", true);
test(CLOCK_MONOTONIC_COARSE, "monotonic-coarse", true);
test(CLOCK_MONOTONIC_RAW, "monotonic-raw", true);
test(CLOCK_BOOTTIME, "boottime", true);
ksft_exit_pass();
return 0;
}
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SELFTEST_TIMENS_LOG_H__
#define __SELFTEST_TIMENS_LOG_H__
#define pr_msg(fmt, lvl, ...) \
ksft_print_msg("[%s] (%s:%d)\t" fmt "\n", \
lvl, __FILE__, __LINE__, ##__VA_ARGS__)
#define pr_p(func, fmt, ...) func(fmt ": %m", ##__VA_ARGS__)
#define pr_err(fmt, ...) \
({ \
ksft_test_result_error(fmt "\n", ##__VA_ARGS__); \
-1; \
})
#define pr_fail(fmt, ...) \
({ \
ksft_test_result_fail(fmt, ##__VA_ARGS__); \
-1; \
})
#define pr_perror(fmt, ...) pr_p(pr_err, fmt, ##__VA_ARGS__)
#endif
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <sched.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "log.h"
#include "timens.h"
/*
* Test shouldn't be run for a day, so add 10 days to child
* time and check parent's time to be in the same day.
*/
#define MAX_TEST_TIME_SEC (60*5)
#define DAY_IN_SEC (60*60*24)
#define TEN_DAYS_IN_SEC (10*DAY_IN_SEC)
static int child_ns, parent_ns;
static int switch_ns(int fd)
{
if (setns(fd, CLONE_NEWTIME))
return pr_perror("setns()");
return 0;
}
static int init_namespaces(void)
{
char path[] = "/proc/self/ns/time_for_children";
struct stat st1, st2;
parent_ns = open(path, O_RDONLY);
if (parent_ns <= 0)
return pr_perror("Unable to open %s", path);
if (fstat(parent_ns, &st1))
return pr_perror("Unable to stat the parent timens");
if (unshare_timens())
return -1;
child_ns = open(path, O_RDONLY);
if (child_ns <= 0)
return pr_perror("Unable to open %s", path);
if (fstat(child_ns, &st2))
return pr_perror("Unable to stat the timens");
if (st1.st_ino == st2.st_ino)
return pr_err("The same child_ns after CLONE_NEWTIME");
if (_settime(CLOCK_BOOTTIME, TEN_DAYS_IN_SEC))
return -1;
return 0;
}
static int read_proc_uptime(struct timespec *uptime)
{
unsigned long up_sec, up_nsec;
FILE *proc;
proc = fopen("/proc/uptime", "r");
if (proc == NULL) {
pr_perror("Unable to open /proc/uptime");
return -1;
}
if (fscanf(proc, "%lu.%02lu", &up_sec, &up_nsec) != 2) {
if (errno) {
pr_perror("fscanf");
return -errno;
}
pr_err("failed to parse /proc/uptime");
return -1;
}
fclose(proc);
uptime->tv_sec = up_sec;
uptime->tv_nsec = up_nsec;
return 0;
}
static int read_proc_stat_btime(unsigned long long *boottime_sec)
{
FILE *proc;
char line_buf[2048];
proc = fopen("/proc/stat", "r");
if (proc == NULL) {
pr_perror("Unable to open /proc/stat");
return -1;
}
while (fgets(line_buf, 2048, proc)) {
if (sscanf(line_buf, "btime %llu", boottime_sec) != 1)
continue;
fclose(proc);
return 0;
}
if (errno) {
pr_perror("fscanf");
fclose(proc);
return -errno;
}
pr_err("failed to parse /proc/stat");
fclose(proc);
return -1;
}
static int check_uptime(void)
{
struct timespec uptime_new, uptime_old;
time_t uptime_expected;
double prec = MAX_TEST_TIME_SEC;
if (switch_ns(parent_ns))
return pr_err("switch_ns(%d)", parent_ns);
if (read_proc_uptime(&uptime_old))
return 1;
if (switch_ns(child_ns))
return pr_err("switch_ns(%d)", child_ns);
if (read_proc_uptime(&uptime_new))
return 1;
uptime_expected = uptime_old.tv_sec + TEN_DAYS_IN_SEC;
if (fabs(difftime(uptime_new.tv_sec, uptime_expected)) > prec) {
pr_fail("uptime in /proc/uptime: old %ld, new %ld [%ld]",
uptime_old.tv_sec, uptime_new.tv_sec,
uptime_old.tv_sec + TEN_DAYS_IN_SEC);
return 1;
}
ksft_test_result_pass("Passed for /proc/uptime\n");
return 0;
}
static int check_stat_btime(void)
{
unsigned long long btime_new, btime_old;
unsigned long long btime_expected;
if (switch_ns(parent_ns))
return pr_err("switch_ns(%d)", parent_ns);
if (read_proc_stat_btime(&btime_old))
return 1;
if (switch_ns(child_ns))
return pr_err("switch_ns(%d)", child_ns);
if (read_proc_stat_btime(&btime_new))
return 1;
btime_expected = btime_old - TEN_DAYS_IN_SEC;
if (btime_new != btime_expected) {
pr_fail("btime in /proc/stat: old %llu, new %llu [%llu]",
btime_old, btime_new, btime_expected);
return 1;
}
ksft_test_result_pass("Passed for /proc/stat btime\n");
return 0;
}
int main(int argc, char *argv[])
{
int ret = 0;
ksft_print_header();
nscheck();
ksft_set_plan(2);
if (init_namespaces())
return 1;
ret |= check_uptime();
ret |= check_stat_btime();
if (ret)
ksft_exit_fail();
ksft_exit_pass();
return ret;
}
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include "log.h"
#include "timens.h"
/*
* Test shouldn't be run for a day, so add 10 days to child
* time and check parent's time to be in the same day.
*/
#define DAY_IN_SEC (60*60*24)
#define TEN_DAYS_IN_SEC (10*DAY_IN_SEC)
struct test_clock {
clockid_t id;
char *name;
/*
* off_id is -1 if a clock has own offset, or it contains an index
* which contains a right offset of this clock.
*/
int off_id;
time_t offset;
};
#define ct(clock, off_id) { clock, #clock, off_id }
static struct test_clock clocks[] = {
ct(CLOCK_BOOTTIME, -1),
ct(CLOCK_BOOTTIME_ALARM, 1),
ct(CLOCK_MONOTONIC, -1),
ct(CLOCK_MONOTONIC_COARSE, 1),
ct(CLOCK_MONOTONIC_RAW, 1),
};
#undef ct
static int child_ns, parent_ns = -1;
static int switch_ns(int fd)
{
if (setns(fd, CLONE_NEWTIME)) {
pr_perror("setns()");
return -1;
}
return 0;
}
static int init_namespaces(void)
{
char path[] = "/proc/self/ns/time_for_children";
struct stat st1, st2;
if (parent_ns == -1) {
parent_ns = open(path, O_RDONLY);
if (parent_ns <= 0)
return pr_perror("Unable to open %s", path);
}
if (fstat(parent_ns, &st1))
return pr_perror("Unable to stat the parent timens");
if (unshare_timens())
return -1;
child_ns = open(path, O_RDONLY);
if (child_ns <= 0)
return pr_perror("Unable to open %s", path);
if (fstat(child_ns, &st2))
return pr_perror("Unable to stat the timens");
if (st1.st_ino == st2.st_ino)
return pr_perror("The same child_ns after CLONE_NEWTIME");
return 0;
}
static int test_gettime(clockid_t clock_index, bool raw_syscall, time_t offset)
{
struct timespec child_ts_new, parent_ts_old, cur_ts;
char *entry = raw_syscall ? "syscall" : "vdso";
double precision = 0.0;
if (check_skip(clocks[clock_index].id))
return 0;
switch (clocks[clock_index].id) {
case CLOCK_MONOTONIC_COARSE:
case CLOCK_MONOTONIC_RAW:
precision = -2.0;
break;
}
if (switch_ns(parent_ns))
return pr_err("switch_ns(%d)", child_ns);
if (_gettime(clocks[clock_index].id, &parent_ts_old, raw_syscall))
return -1;
child_ts_new.tv_nsec = parent_ts_old.tv_nsec;
child_ts_new.tv_sec = parent_ts_old.tv_sec + offset;
if (switch_ns(child_ns))
return pr_err("switch_ns(%d)", child_ns);
if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
return -1;
if (difftime(cur_ts.tv_sec, child_ts_new.tv_sec) < precision) {
ksft_test_result_fail(
"Child's %s (%s) time has not changed: %lu -> %lu [%lu]\n",
clocks[clock_index].name, entry, parent_ts_old.tv_sec,
child_ts_new.tv_sec, cur_ts.tv_sec);
return -1;
}
if (switch_ns(parent_ns))
return pr_err("switch_ns(%d)", parent_ns);
if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
return -1;
if (difftime(cur_ts.tv_sec, parent_ts_old.tv_sec) > DAY_IN_SEC) {
ksft_test_result_fail(
"Parent's %s (%s) time has changed: %lu -> %lu [%lu]\n",
clocks[clock_index].name, entry, parent_ts_old.tv_sec,
child_ts_new.tv_sec, cur_ts.tv_sec);
/* Let's play nice and put it closer to original */
clock_settime(clocks[clock_index].id, &cur_ts);
return -1;
}
ksft_test_result_pass("Passed for %s (%s)\n",
clocks[clock_index].name, entry);
return 0;
}
int main(int argc, char *argv[])
{
unsigned int i;
time_t offset;
int ret = 0;
ksft_print_header();
nscheck();
check_supported_timers();
ksft_set_plan(ARRAY_SIZE(clocks) * 2);
if (init_namespaces())
return 1;
/* Offsets have to be set before tasks enter the namespace. */
for (i = 0; i < ARRAY_SIZE(clocks); i++) {
if (clocks[i].off_id != -1)
continue;
offset = TEN_DAYS_IN_SEC + i * 1000;
clocks[i].offset = offset;
if (_settime(clocks[i].id, offset))
return 1;
}
for (i = 0; i < ARRAY_SIZE(clocks); i++) {
if (clocks[i].off_id != -1)
offset = clocks[clocks[i].off_id].offset;
else
offset = clocks[i].offset;
ret |= test_gettime(i, true, offset);
ret |= test_gettime(i, false, offset);
}
if (ret)
ksft_exit_fail();
ksft_exit_pass();
return !!ret;
}
@@ -0,0 +1,111 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __TIMENS_H__
#define __TIMENS_H__
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include "kselftest.h"
#ifndef CLONE_NEWTIME
# define CLONE_NEWTIME 0x00000080
#endif
static int config_posix_timers = true;
static int config_alarm_timers = true;
static inline void check_supported_timers(void)
{
struct timespec ts;
if (timer_create(-1, 0, 0) == -1 && errno == ENOSYS)
config_posix_timers = false;
if (clock_gettime(CLOCK_BOOTTIME_ALARM, &ts) == -1 && errno == EINVAL)
config_alarm_timers = false;
}
static inline bool check_skip(int clockid)
{
if (!config_alarm_timers && clockid == CLOCK_BOOTTIME_ALARM) {
ksft_test_result_skip("CLOCK_BOOTTIME_ALARM isn't supported\n");
return true;
}
if (config_posix_timers)
return false;
switch (clockid) {
/* Only these clocks are supported without CONFIG_POSIX_TIMERS. */
case CLOCK_BOOTTIME:
case CLOCK_MONOTONIC:
case CLOCK_REALTIME:
return false;
default:
ksft_test_result_skip("Posix Clocks & timers are not supported\n");
return true;
}
return false;
}
static inline int unshare_timens(void)
{
if (unshare(CLONE_NEWTIME)) {
if (errno == EPERM)
ksft_exit_skip("need to run as root\n");
return pr_perror("Can't unshare() timens");
}
return 0;
}
static inline int _settime(clockid_t clk_id, time_t offset)
{
int fd, len;
char buf[4096];
if (clk_id == CLOCK_MONOTONIC_COARSE || clk_id == CLOCK_MONOTONIC_RAW)
clk_id = CLOCK_MONOTONIC;
len = snprintf(buf, sizeof(buf), "%d %ld 0", clk_id, offset);
fd = open("/proc/self/timens_offsets", O_WRONLY);
if (fd < 0)
return pr_perror("/proc/self/timens_offsets");
if (write(fd, buf, len) != len)
return pr_perror("/proc/self/timens_offsets");
close(fd);
return 0;
}
static inline int _gettime(clockid_t clk_id, struct timespec *res, bool raw_syscall)
{
int err;
if (!raw_syscall) {
if (clock_gettime(clk_id, res)) {
pr_perror("clock_gettime(%d)", (int)clk_id);
return -1;
}
return 0;
}
err = syscall(SYS_clock_gettime, clk_id, res);
if (err)
pr_perror("syscall(SYS_clock_gettime(%d))", (int)clk_id);
return err;
}
static inline void nscheck(void)
{
if (access("/proc/self/ns/time", F_OK) < 0)
ksft_exit_skip("Time namespaces are not supported\n");
}
#endif
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sched.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include "log.h"
#include "timens.h"
static int run_test(int clockid, struct timespec now)
{
struct itimerspec new_value;
long long elapsed;
timer_t fd;
int i;
if (check_skip(clockid))
return 0;
for (i = 0; i < 2; i++) {
struct sigevent sevp = {.sigev_notify = SIGEV_NONE};
int flags = 0;
new_value.it_value.tv_sec = 3600;
new_value.it_value.tv_nsec = 0;
new_value.it_interval.tv_sec = 1;
new_value.it_interval.tv_nsec = 0;
if (i == 1) {
new_value.it_value.tv_sec += now.tv_sec;
new_value.it_value.tv_nsec += now.tv_nsec;
}
if (timer_create(clockid, &sevp, &fd) == -1) {
if (errno == ENOSYS) {
ksft_test_result_skip("Posix Clocks & timers are supported\n");
return 0;
}
return pr_perror("timerfd_create");
}
if (i == 1)
flags |= TIMER_ABSTIME;
if (timer_settime(fd, flags, &new_value, NULL) == -1)
return pr_perror("timerfd_settime");
if (timer_gettime(fd, &new_value) == -1)
return pr_perror("timerfd_gettime");
elapsed = new_value.it_value.tv_sec;
if (llabs(elapsed - 3600) > 60) {
ksft_test_result_fail("clockid: %d elapsed: %lld\n",
clockid, elapsed);
return 1;
}
}
ksft_test_result_pass("clockid=%d\n", clockid);
return 0;
}
int main(int argc, char *argv[])
{
int ret, status, len, fd;
char buf[4096];
pid_t pid;
struct timespec btime_now, mtime_now;
ksft_print_header();
nscheck();
check_supported_timers();
ksft_set_plan(3);
clock_gettime(CLOCK_MONOTONIC, &mtime_now);
clock_gettime(CLOCK_BOOTTIME, &btime_now);
if (unshare_timens())
return 1;
len = snprintf(buf, sizeof(buf), "%d %d 0\n%d %d 0",
CLOCK_MONOTONIC, 70 * 24 * 3600,
CLOCK_BOOTTIME, 9 * 24 * 3600);
fd = open("/proc/self/timens_offsets", O_WRONLY);
if (fd < 0)
return pr_perror("/proc/self/timens_offsets");
if (write(fd, buf, len) != len)
return pr_perror("/proc/self/timens_offsets");
close(fd);
mtime_now.tv_sec += 70 * 24 * 3600;
btime_now.tv_sec += 9 * 24 * 3600;
pid = fork();
if (pid < 0)
return pr_perror("Unable to fork");
if (pid == 0) {
ret = 0;
ret |= run_test(CLOCK_BOOTTIME, btime_now);
ret |= run_test(CLOCK_MONOTONIC, mtime_now);
ret |= run_test(CLOCK_BOOTTIME_ALARM, btime_now);
if (ret)
ksft_exit_fail();
ksft_exit_pass();
return ret;
}
if (waitpid(pid, &status, 0) != pid)
return pr_perror("Unable to wait the child process");
if (WIFEXITED(status))
return WEXITSTATUS(status);
return 1;
}
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sched.h>
#include <sys/timerfd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "log.h"
#include "timens.h"
static int tclock_gettime(clockid_t clockid, struct timespec *now)
{
if (clockid == CLOCK_BOOTTIME_ALARM)
clockid = CLOCK_BOOTTIME;
return clock_gettime(clockid, now);
}
static int run_test(int clockid, struct timespec now)
{
struct itimerspec new_value;
long long elapsed;
int fd, i;
if (check_skip(clockid))
return 0;
if (tclock_gettime(clockid, &now))
return pr_perror("clock_gettime(%d)", clockid);
for (i = 0; i < 2; i++) {
int flags = 0;
new_value.it_value.tv_sec = 3600;
new_value.it_value.tv_nsec = 0;
new_value.it_interval.tv_sec = 1;
new_value.it_interval.tv_nsec = 0;
if (i == 1) {
new_value.it_value.tv_sec += now.tv_sec;
new_value.it_value.tv_nsec += now.tv_nsec;
}
fd = timerfd_create(clockid, 0);
if (fd == -1)
return pr_perror("timerfd_create(%d)", clockid);
if (i == 1)
flags |= TFD_TIMER_ABSTIME;
if (timerfd_settime(fd, flags, &new_value, NULL))
return pr_perror("timerfd_settime(%d)", clockid);
if (timerfd_gettime(fd, &new_value))
return pr_perror("timerfd_gettime(%d)", clockid);
elapsed = new_value.it_value.tv_sec;
if (llabs(elapsed - 3600) > 60) {
ksft_test_result_fail("clockid: %d elapsed: %lld\n",
clockid, elapsed);
return 1;
}
close(fd);
}
ksft_test_result_pass("clockid=%d\n", clockid);
return 0;
}
int main(int argc, char *argv[])
{
int ret, status, len, fd;
char buf[4096];
pid_t pid;
struct timespec btime_now, mtime_now;
ksft_print_header();
nscheck();
check_supported_timers();
ksft_set_plan(3);
clock_gettime(CLOCK_MONOTONIC, &mtime_now);
clock_gettime(CLOCK_BOOTTIME, &btime_now);
if (unshare_timens())
return 1;
len = snprintf(buf, sizeof(buf), "%d %d 0\n%d %d 0",
CLOCK_MONOTONIC, 70 * 24 * 3600,
CLOCK_BOOTTIME, 9 * 24 * 3600);
fd = open("/proc/self/timens_offsets", O_WRONLY);
if (fd < 0)
return pr_perror("/proc/self/timens_offsets");
if (write(fd, buf, len) != len)
return pr_perror("/proc/self/timens_offsets");
close(fd);
mtime_now.tv_sec += 70 * 24 * 3600;
btime_now.tv_sec += 9 * 24 * 3600;
pid = fork();
if (pid < 0)
return pr_perror("Unable to fork");
if (pid == 0) {
ret = 0;
ret |= run_test(CLOCK_BOOTTIME, btime_now);
ret |= run_test(CLOCK_MONOTONIC, mtime_now);
ret |= run_test(CLOCK_BOOTTIME_ALARM, btime_now);
if (ret)
ksft_exit_fail();
ksft_exit_pass();
return ret;
}
if (waitpid(pid, &status, 0) != pid)
return pr_perror("Unable to wait the child process");
if (WIFEXITED(status))
return WEXITSTATUS(status);
return 1;
}
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "log.h"
#include "timens.h"
#define OFFSET (36000)
struct thread_args {
char *tst_name;
struct timespec *now;
};
static void *tcheck(void *_args)
{
struct thread_args *args = _args;
struct timespec *now = args->now, tst;
int i;
for (i = 0; i < 2; i++) {
_gettime(CLOCK_MONOTONIC, &tst, i);
if (labs(tst.tv_sec - now->tv_sec) > 5) {
pr_fail("%s: in-thread: unexpected value: %ld (%ld)\n",
args->tst_name, tst.tv_sec, now->tv_sec);
return (void *)1UL;
}
}
return NULL;
}
static int check_in_thread(char *tst_name, struct timespec *now)
{
struct thread_args args = {
.tst_name = tst_name,
.now = now,
};
pthread_t th;
void *retval;
if (pthread_create(&th, NULL, tcheck, &args))
return pr_perror("thread");
if (pthread_join(th, &retval))
return pr_perror("pthread_join");
return !(retval == NULL);
}
static int check(char *tst_name, struct timespec *now)
{
struct timespec tst;
int i;
for (i = 0; i < 2; i++) {
_gettime(CLOCK_MONOTONIC, &tst, i);
if (labs(tst.tv_sec - now->tv_sec) > 5)
return pr_fail("%s: unexpected value: %ld (%ld)\n",
tst_name, tst.tv_sec, now->tv_sec);
}
if (check_in_thread(tst_name, now))
return 1;
ksft_test_result_pass("%s\n", tst_name);
return 0;
}
int main(int argc, char *argv[])
{
struct timespec now;
int status;
pid_t pid;
if (argc > 1) {
char *endptr;
ksft_cnt.ksft_pass = 1;
now.tv_sec = strtoul(argv[1], &endptr, 0);
if (*endptr != 0)
return pr_perror("strtoul");
return check("child after exec", &now);
}
ksft_print_header();
nscheck();
ksft_set_plan(4);
clock_gettime(CLOCK_MONOTONIC, &now);
if (unshare_timens())
return 1;
if (_settime(CLOCK_MONOTONIC, OFFSET))
return 1;
if (check("parent before vfork", &now))
return 1;
pid = vfork();
if (pid < 0)
return pr_perror("fork");
if (pid == 0) {
char now_str[64];
char *cargv[] = {"exec", now_str, NULL};
char *cenv[] = {NULL};
/* Check for proper vvar offsets after execve. */
snprintf(now_str, sizeof(now_str), "%ld", now.tv_sec + OFFSET);
execve("/proc/self/exe", cargv, cenv);
pr_perror("execve");
_exit(1);
}
if (waitpid(pid, &status, 0) != pid)
return pr_perror("waitpid");
if (status)
ksft_exit_fail();
ksft_inc_pass_cnt();
ksft_test_result_pass("wait for child\n");
/* Check that we are still in the source timens. */
if (check("parent after vfork", &now))
return 1;
ksft_exit_pass();
return 0;
}