2020-10-11 18:06:52

by Michael Weiß

[permalink] [raw]
Subject: [PATCH v3 0/3] time namespace aware system boot time

Time namespaces make it possible to virtualize time inside of
containers, e.g., it is feasible to reset the uptime of a container
to zero by setting the time namespace offset for boottime to the
negated current value of the CLOCK_BOOTTIME.

However, the boot time stamp provided by getboottime64() does not
take care of time namespaces. The resulting boot time stamp 'btime'
provided by /proc/stat does not show a plausible time stamp inside
the time namespace of a container.

We address this by shifting the value returned by getboottime64()
by subtracting the boottime offset of the time namespace.
(A selftest to check the expected /proc/stat 'btime' inside the
namespace is provided.)

Further, to avoid to show processes as time travelers inside of the
time namespace the boottime offset then needs to be added to the
start_bootime provided by the task_struct.

v3 Changes:
leave getboottime64() unchanged and shift the boot timestamp in
'fs/proc/stat.c' as result of the discussion with Andrei and Thomas.

v2 Changes:
Fixed compile errors with TIME_NS not set in config
Reported-by: kernel test robot <[email protected]>

Michael Weiß (3):
timens: additional helper function to add boottime in nsec
fs/proc: apply the time namespace offset to /proc/stat btime
selftests/timens: added selftest for /proc/stat btime

fs/proc/array.c | 6 ++-
fs/proc/stat.c | 17 +++++++-
include/linux/time_namespace.h | 13 ++++++
tools/testing/selftests/timens/procfs.c | 58 ++++++++++++++++++++++++-
4 files changed, 90 insertions(+), 4 deletions(-)

--
2.20.1


2020-10-11 20:24:04

by Michael Weiß

[permalink] [raw]
Subject: [PATCH v3 3/3] selftests/timens: added selftest for /proc/stat btime

Test that btime value of /proc/stat is as expected in the time namespace
using a simple parser to get btime from /proc/stat.

Signed-off-by: Michael Weiß <[email protected]>
---
tools/testing/selftests/timens/procfs.c | 58 ++++++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/timens/procfs.c b/tools/testing/selftests/timens/procfs.c
index 7f14f0fdac84..f2519154208a 100644
--- a/tools/testing/selftests/timens/procfs.c
+++ b/tools/testing/selftests/timens/procfs.c
@@ -93,6 +93,33 @@ static int read_proc_uptime(struct timespec *uptime)
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;
@@ -123,18 +150,47 @@ static int check_uptime(void)
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;

nscheck();

- ksft_set_plan(1);
+ ksft_set_plan(2);

if (init_namespaces())
return 1;

ret |= check_uptime();
+ ret |= check_stat_btime();

if (ret)
ksft_exit_fail();
--
2.20.1