All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
From: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
To: khorenko@virtuozzo.com
Cc: devel@openvz.org
Subject: [Devel] [PATCH hci-8.1 4/4] timens: freeze the offsets on the first join without the vDSO too
Date: Fri,  9 Oct 2026 16:58:37 +0300	[thread overview]
Message-ID: <20261009135929.3274885-5-eva.kurchatova@virtuozzo.com> (raw)
In-Reply-To: <20261009135929.3274885-1-eva.kurchatova@virtuozzo.com>

A time namespace's offsets may be written until the first task joins
it; from then on they are read without a lock, so that join freezes
them. The freeze sat in the code that fills the namespace's vDSO
page, and since 1b6c89285d37 ("timens: Remove dependency on the vDSO")
a kernel without a generic vDSO, User Mode Linux now, never froze: the
owner could rewrite the offsets under the tasks already running there,
and the clocks they read moved with them.

Freeze in timens_commit() itself and fill the vDSO page from there,
where there is one.

Fixes: 1b6c89285d37 ("timens: Remove dependency on the vDSO")
https://virtuozzo.atlassian.net/browse/VSTOR-148112
Feature: fix ms/timens
Signed-off-by: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
---
 include/linux/time_namespace.h   | 11 ++++++-----
 kernel/time/namespace.c          | 28 +++++++++++++++++++++++++++
 kernel/time/namespace_internal.h | 10 ++++++++++
 kernel/time/namespace_vdso.c     | 33 +++++---------------------------
 4 files changed, 49 insertions(+), 33 deletions(-)

diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
index 6c1872e7bb73..a8b4403e8630 100644
--- a/include/linux/time_namespace.h
+++ b/include/linux/time_namespace.h
@@ -46,6 +46,7 @@ struct time_namespace *copy_time_ns(unsigned long flags,
 				    struct time_namespace *old_ns);
 void free_time_ns(struct time_namespace *ns);
 void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);
+void timens_commit(struct task_struct *tsk, struct time_namespace *ns);
 
 static inline void put_time_ns(struct time_namespace *ns)
 {
@@ -131,6 +132,11 @@ static inline void timens_on_fork(struct nsproxy *nsproxy,
 	return;
 }
 
+static inline void timens_commit(struct task_struct *tsk,
+				 struct time_namespace *ns)
+{
+}
+
 static inline void timens_add_monotonic(struct timespec64 *ts) { }
 static inline void timens_add_boottime(struct timespec64 *ts) { }
 
@@ -148,13 +154,8 @@ static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim)
 #endif
 
 #ifdef CONFIG_TIME_NS_VDSO
-extern void timens_commit(struct task_struct *tsk, struct time_namespace *ns);
 struct page *find_timens_vvar_page(struct vm_area_struct *vma);
 #else /* !CONFIG_TIME_NS_VDSO */
-static inline void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
-{
-}
-
 static inline struct page *find_timens_vvar_page(struct vm_area_struct *vma)
 {
 	return NULL;
diff --git a/kernel/time/namespace.c b/kernel/time/namespace.c
index cf95f9e0ad94..9bd21eb8bf2d 100644
--- a/kernel/time/namespace.c
+++ b/kernel/time/namespace.c
@@ -192,6 +192,34 @@ static void timens_put(struct ns_common *ns)
 	put_time_ns(to_time_ns(ns));
 }
 
+/*
+ * The offsets are read without a lock once a task runs in the namespace,
+ * so the first task to join freezes them, and sets up the vDSO page from
+ * them where there is one.
+ */
+static void timens_freeze_offsets(struct time_namespace *ns)
+{
+	if (ns == &init_time_ns)
+		return;
+
+	/* Fast-path, taken by every task in namespace except the first. */
+	if (likely(ns->frozen_offsets))
+		return;
+
+	guard(mutex)(&timens_offset_lock);
+	if (ns->frozen_offsets)
+		return;
+
+	timens_vdso_setup_vvar_page(ns);
+	ns->frozen_offsets = true;
+}
+
+void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
+{
+	timens_freeze_offsets(ns);
+	timens_vdso_join(tsk, ns);
+}
+
 static int timens_install(struct nsset *nsset, struct ns_common *new)
 {
 	struct nsproxy *nsproxy = nsset->nsproxy;
diff --git a/kernel/time/namespace_internal.h b/kernel/time/namespace_internal.h
index b37ba179f43b..564c22b639dc 100644
--- a/kernel/time/namespace_internal.h
+++ b/kernel/time/namespace_internal.h
@@ -4,6 +4,7 @@
 
 #include <linux/mutex.h>
 
+struct task_struct;
 struct time_namespace;
 
 /*
@@ -15,6 +16,8 @@ extern struct mutex timens_offset_lock;
 #ifdef CONFIG_TIME_NS_VDSO
 int timens_vdso_alloc_vvar_page(struct time_namespace *ns);
 void timens_vdso_free_vvar_page(struct time_namespace *ns);
+void timens_vdso_setup_vvar_page(struct time_namespace *ns);
+void timens_vdso_join(struct task_struct *task, struct time_namespace *ns);
 #else /* !CONFIG_TIME_NS_VDSO */
 static inline int timens_vdso_alloc_vvar_page(struct time_namespace *ns)
 {
@@ -23,6 +26,13 @@ static inline int timens_vdso_alloc_vvar_page(struct time_namespace *ns)
 static inline void timens_vdso_free_vvar_page(struct time_namespace *ns)
 {
 }
+static inline void timens_vdso_setup_vvar_page(struct time_namespace *ns)
+{
+}
+static inline void timens_vdso_join(struct task_struct *task,
+				    struct time_namespace *ns)
+{
+}
 #endif /* CONFIG_TIME_NS_VDSO */
 
 #endif /* _TIME_NAMESPACE_INTERNAL_H */
diff --git a/kernel/time/namespace_vdso.c b/kernel/time/namespace_vdso.c
index 08d69270eb2c..8a2fc9d23687 100644
--- a/kernel/time/namespace_vdso.c
+++ b/kernel/time/namespace_vdso.c
@@ -86,29 +86,13 @@ struct page *find_timens_vvar_page(struct vm_area_struct *vma)
 	return NULL;
 }
 
-static void timens_set_vvar_page(struct task_struct *task,
-				struct time_namespace *ns)
+/* Called once per namespace, under timens_offset_lock, before any task reads */
+void timens_vdso_setup_vvar_page(struct time_namespace *ns)
 {
-	struct vdso_time_data *vdata;
-	struct vdso_clock *vc;
+	struct vdso_time_data *vdata = page_address(ns->vvar_page);
+	struct vdso_clock *vc = vdata->clock_data;
 	unsigned int i;
 
-	if (ns == &init_time_ns)
-		return;
-
-	/* Fast-path, taken by every task in namespace except the first. */
-	if (likely(ns->frozen_offsets))
-		return;
-
-	guard(mutex)(&timens_offset_lock);
-	/* Nothing to-do: vvar_page has been already initialized. */
-	if (ns->frozen_offsets)
-		return;
-
-	ns->frozen_offsets = true;
-	vdata = page_address(ns->vvar_page);
-	vc = vdata->clock_data;
-
 	for (i = 0; i < CS_BASES; i++)
 		timens_setup_vdso_clock_data(&vc[i], ns);
 }
@@ -120,7 +104,7 @@ static void timens_set_vvar_page(struct task_struct *task,
  * corresponding layout.
  * See also the comment near timens_setup_vdso_clock_data() for details.
  */
-static int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
+void timens_vdso_join(struct task_struct *task, struct time_namespace *ns)
 {
 	struct mm_struct *mm = task->mm;
 	struct vm_area_struct *vma;
@@ -131,13 +115,6 @@ static int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
 		if (vma_is_special_mapping(vma, &vdso_vvar_mapping))
 			zap_vma_pages(vma);
 	}
-	return 0;
-}
-
-void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
-{
-	timens_set_vvar_page(tsk, ns);
-	vdso_join_timens(tsk, ns);
 }
 
 int timens_vdso_alloc_vvar_page(struct time_namespace *ns)
-- 
2.56.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

  parent reply	other threads:[~2026-10-09 14:01 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-09 13:58 [Devel] [PATCH hci-8.1 0/4] timens: Remove dependency on the vDSO Eva Kurchatova
2026-10-09 13:58 ` [Devel] [PATCH hci-8.1 1/4] ms/vdso: Drop Kconfig GENERIC_VDSO_TIME_NS Eva Kurchatova
2026-10-09 14:04   ` no-reply
2026-10-09 13:58 ` [Devel] [PATCH hci-8.1 2/4] ms/vdso/timens: Move functions to new file Eva Kurchatova
2026-10-09 14:03   ` no-reply
2026-10-09 13:58 ` [Devel] [PATCH hci-8.1 3/4] ms/timens: Remove dependency on the vDSO Eva Kurchatova
2026-10-09 14:06   ` no-reply
2026-10-09 13:58 ` Eva Kurchatova [this message]
2026-10-09 14:06   ` [Devel] [PATCH hci-8.1 4/4] timens: freeze the offsets on the first join without the vDSO too no-reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261009135929.3274885-5-eva.kurchatova@virtuozzo.com \
    --to=eva.kurchatova@virtuozzo.com \
    --cc=devel@openvz.org \
    --cc=khorenko@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.