1769071acSAndrei Vagin // SPDX-License-Identifier: GPL-2.0
2769071acSAndrei Vagin /*
3769071acSAndrei Vagin * Author: Andrei Vagin <[email protected]>
4769071acSAndrei Vagin * Author: Dmitry Safonov <[email protected]>
5769071acSAndrei Vagin */
6769071acSAndrei Vagin
7769071acSAndrei Vagin #include <linux/time_namespace.h>
8769071acSAndrei Vagin #include <linux/user_namespace.h>
9769071acSAndrei Vagin #include <linux/sched/signal.h>
10769071acSAndrei Vagin #include <linux/sched/task.h>
112d6b01bdSThomas Gleixner #include <linux/clocksource.h>
1204a8682aSAndrei Vagin #include <linux/seq_file.h>
13769071acSAndrei Vagin #include <linux/proc_ns.h>
14769071acSAndrei Vagin #include <linux/export.h>
15769071acSAndrei Vagin #include <linux/time.h>
16769071acSAndrei Vagin #include <linux/slab.h>
17769071acSAndrei Vagin #include <linux/cred.h>
18769071acSAndrei Vagin #include <linux/err.h>
19af993f58SAndrei Vagin #include <linux/mm.h>
20769071acSAndrei Vagin
21afaa7b5aSDmitry Safonov #include <vdso/datapage.h>
22afaa7b5aSDmitry Safonov
do_timens_ktime_to_host(clockid_t clockid,ktime_t tim,struct timens_offsets * ns_offsets)2389dd8eecSAndrei Vagin ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim,
2489dd8eecSAndrei Vagin struct timens_offsets *ns_offsets)
2589dd8eecSAndrei Vagin {
2689dd8eecSAndrei Vagin ktime_t offset;
2789dd8eecSAndrei Vagin
2889dd8eecSAndrei Vagin switch (clockid) {
2989dd8eecSAndrei Vagin case CLOCK_MONOTONIC:
3089dd8eecSAndrei Vagin offset = timespec64_to_ktime(ns_offsets->monotonic);
3189dd8eecSAndrei Vagin break;
3289dd8eecSAndrei Vagin case CLOCK_BOOTTIME:
3389dd8eecSAndrei Vagin case CLOCK_BOOTTIME_ALARM:
3489dd8eecSAndrei Vagin offset = timespec64_to_ktime(ns_offsets->boottime);
3589dd8eecSAndrei Vagin break;
3689dd8eecSAndrei Vagin default:
3789dd8eecSAndrei Vagin return tim;
3889dd8eecSAndrei Vagin }
3989dd8eecSAndrei Vagin
4089dd8eecSAndrei Vagin /*
4189dd8eecSAndrei Vagin * Check that @tim value is in [offset, KTIME_MAX + offset]
4289dd8eecSAndrei Vagin * and subtract offset.
4389dd8eecSAndrei Vagin */
4489dd8eecSAndrei Vagin if (tim < offset) {
4589dd8eecSAndrei Vagin /*
4689dd8eecSAndrei Vagin * User can specify @tim *absolute* value - if it's lesser than
4789dd8eecSAndrei Vagin * the time namespace's offset - it's already expired.
4889dd8eecSAndrei Vagin */
4989dd8eecSAndrei Vagin tim = 0;
5089dd8eecSAndrei Vagin } else {
5189dd8eecSAndrei Vagin tim = ktime_sub(tim, offset);
5289dd8eecSAndrei Vagin if (unlikely(tim > KTIME_MAX))
5389dd8eecSAndrei Vagin tim = KTIME_MAX;
5489dd8eecSAndrei Vagin }
5589dd8eecSAndrei Vagin
5689dd8eecSAndrei Vagin return tim;
5789dd8eecSAndrei Vagin }
5889dd8eecSAndrei Vagin
inc_time_namespaces(struct user_namespace * ns)59769071acSAndrei Vagin static struct ucounts *inc_time_namespaces(struct user_namespace *ns)
60769071acSAndrei Vagin {
61769071acSAndrei Vagin return inc_ucount(ns, current_euid(), UCOUNT_TIME_NAMESPACES);
62769071acSAndrei Vagin }
63769071acSAndrei Vagin
dec_time_namespaces(struct ucounts * ucounts)64769071acSAndrei Vagin static void dec_time_namespaces(struct ucounts *ucounts)
65769071acSAndrei Vagin {
66769071acSAndrei Vagin dec_ucount(ucounts, UCOUNT_TIME_NAMESPACES);
67769071acSAndrei Vagin }
68769071acSAndrei Vagin
69769071acSAndrei Vagin /**
70769071acSAndrei Vagin * clone_time_ns - Clone a time namespace
71769071acSAndrei Vagin * @user_ns: User namespace which owns a new namespace.
72769071acSAndrei Vagin * @old_ns: Namespace to clone
73769071acSAndrei Vagin *
74769071acSAndrei Vagin * Clone @old_ns and set the clone refcount to 1
75769071acSAndrei Vagin *
76769071acSAndrei Vagin * Return: The new namespace or ERR_PTR.
77769071acSAndrei Vagin */
clone_time_ns(struct user_namespace * user_ns,struct time_namespace * old_ns)78769071acSAndrei Vagin static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
79769071acSAndrei Vagin struct time_namespace *old_ns)
80769071acSAndrei Vagin {
81769071acSAndrei Vagin struct time_namespace *ns;
82769071acSAndrei Vagin struct ucounts *ucounts;
83769071acSAndrei Vagin int err;
84769071acSAndrei Vagin
85769071acSAndrei Vagin err = -ENOSPC;
86769071acSAndrei Vagin ucounts = inc_time_namespaces(user_ns);
87769071acSAndrei Vagin if (!ucounts)
88769071acSAndrei Vagin goto fail;
89769071acSAndrei Vagin
90769071acSAndrei Vagin err = -ENOMEM;
9130acd0bdSVasily Averin ns = kmalloc(sizeof(*ns), GFP_KERNEL_ACCOUNT);
92769071acSAndrei Vagin if (!ns)
93769071acSAndrei Vagin goto fail_dec;
94769071acSAndrei Vagin
9528c41efdSKirill Tkhai refcount_set(&ns->ns.count, 1);
96769071acSAndrei Vagin
9730acd0bdSVasily Averin ns->vvar_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
98afaa7b5aSDmitry Safonov if (!ns->vvar_page)
99afaa7b5aSDmitry Safonov goto fail_free;
100afaa7b5aSDmitry Safonov
101769071acSAndrei Vagin err = ns_alloc_inum(&ns->ns);
102769071acSAndrei Vagin if (err)
103afaa7b5aSDmitry Safonov goto fail_free_page;
104769071acSAndrei Vagin
105769071acSAndrei Vagin ns->ucounts = ucounts;
106769071acSAndrei Vagin ns->ns.ops = &timens_operations;
107769071acSAndrei Vagin ns->user_ns = get_user_ns(user_ns);
108af993f58SAndrei Vagin ns->offsets = old_ns->offsets;
109afaa7b5aSDmitry Safonov ns->frozen_offsets = false;
110769071acSAndrei Vagin return ns;
111769071acSAndrei Vagin
112afaa7b5aSDmitry Safonov fail_free_page:
113afaa7b5aSDmitry Safonov __free_page(ns->vvar_page);
114769071acSAndrei Vagin fail_free:
115769071acSAndrei Vagin kfree(ns);
116769071acSAndrei Vagin fail_dec:
117769071acSAndrei Vagin dec_time_namespaces(ucounts);
118769071acSAndrei Vagin fail:
119769071acSAndrei Vagin return ERR_PTR(err);
120769071acSAndrei Vagin }
121769071acSAndrei Vagin
122769071acSAndrei Vagin /**
123769071acSAndrei Vagin * copy_time_ns - Create timens_for_children from @old_ns
124769071acSAndrei Vagin * @flags: Cloning flags
125769071acSAndrei Vagin * @user_ns: User namespace which owns a new namespace.
126769071acSAndrei Vagin * @old_ns: Namespace to clone
127769071acSAndrei Vagin *
128769071acSAndrei Vagin * If CLONE_NEWTIME specified in @flags, creates a new timens_for_children;
129769071acSAndrei Vagin * adds a refcounter to @old_ns otherwise.
130769071acSAndrei Vagin *
131769071acSAndrei Vagin * Return: timens_for_children namespace or ERR_PTR.
132769071acSAndrei Vagin */
copy_time_ns(unsigned long flags,struct user_namespace * user_ns,struct time_namespace * old_ns)133769071acSAndrei Vagin struct time_namespace *copy_time_ns(unsigned long flags,
134769071acSAndrei Vagin struct user_namespace *user_ns, struct time_namespace *old_ns)
135769071acSAndrei Vagin {
136769071acSAndrei Vagin if (!(flags & CLONE_NEWTIME))
137769071acSAndrei Vagin return get_time_ns(old_ns);
138769071acSAndrei Vagin
139769071acSAndrei Vagin return clone_time_ns(user_ns, old_ns);
140769071acSAndrei Vagin }
141769071acSAndrei Vagin
offset_from_ts(struct timespec64 off)142afaa7b5aSDmitry Safonov static struct timens_offset offset_from_ts(struct timespec64 off)
143afaa7b5aSDmitry Safonov {
144afaa7b5aSDmitry Safonov struct timens_offset ret;
145afaa7b5aSDmitry Safonov
146afaa7b5aSDmitry Safonov ret.sec = off.tv_sec;
147afaa7b5aSDmitry Safonov ret.nsec = off.tv_nsec;
148afaa7b5aSDmitry Safonov
149afaa7b5aSDmitry Safonov return ret;
150afaa7b5aSDmitry Safonov }
151afaa7b5aSDmitry Safonov
152afaa7b5aSDmitry Safonov /*
153afaa7b5aSDmitry Safonov * A time namespace VVAR page has the same layout as the VVAR page which
154afaa7b5aSDmitry Safonov * contains the system wide VDSO data.
155afaa7b5aSDmitry Safonov *
156afaa7b5aSDmitry Safonov * For a normal task the VVAR pages are installed in the normal ordering:
157afaa7b5aSDmitry Safonov * VVAR
158afaa7b5aSDmitry Safonov * PVCLOCK
159afaa7b5aSDmitry Safonov * HVCLOCK
160afaa7b5aSDmitry Safonov * TIMENS <- Not really required
161afaa7b5aSDmitry Safonov *
162afaa7b5aSDmitry Safonov * Now for a timens task the pages are installed in the following order:
163afaa7b5aSDmitry Safonov * TIMENS
164afaa7b5aSDmitry Safonov * PVCLOCK
165afaa7b5aSDmitry Safonov * HVCLOCK
166afaa7b5aSDmitry Safonov * VVAR
167afaa7b5aSDmitry Safonov *
1685911e16cSAnna-Maria Behnsen * The check for vdso_clock->clock_mode is in the unlikely path of
169afaa7b5aSDmitry Safonov * the seq begin magic. So for the non-timens case most of the time
170afaa7b5aSDmitry Safonov * 'seq' is even, so the branch is not taken.
171afaa7b5aSDmitry Safonov *
172afaa7b5aSDmitry Safonov * If 'seq' is odd, i.e. a concurrent update is in progress, the extra check
1735911e16cSAnna-Maria Behnsen * for vdso_clock->clock_mode is a non-issue. The task is spin waiting for the
174afaa7b5aSDmitry Safonov * update to finish and for 'seq' to become even anyway.
175afaa7b5aSDmitry Safonov *
1765911e16cSAnna-Maria Behnsen * Timens page has vdso_clock->clock_mode set to VDSO_CLOCKMODE_TIMENS which
1772d6b01bdSThomas Gleixner * enforces the time namespace handling path.
178afaa7b5aSDmitry Safonov */
timens_setup_vdso_clock_data(struct vdso_clock * vc,struct time_namespace * ns)1795911e16cSAnna-Maria Behnsen static void timens_setup_vdso_clock_data(struct vdso_clock *vc,
180afaa7b5aSDmitry Safonov struct time_namespace *ns)
181afaa7b5aSDmitry Safonov {
1825911e16cSAnna-Maria Behnsen struct timens_offset *offset = vc->offset;
183afaa7b5aSDmitry Safonov struct timens_offset monotonic = offset_from_ts(ns->offsets.monotonic);
184afaa7b5aSDmitry Safonov struct timens_offset boottime = offset_from_ts(ns->offsets.boottime);
185afaa7b5aSDmitry Safonov
1865911e16cSAnna-Maria Behnsen vc->seq = 1;
1875911e16cSAnna-Maria Behnsen vc->clock_mode = VDSO_CLOCKMODE_TIMENS;
188afaa7b5aSDmitry Safonov offset[CLOCK_MONOTONIC] = monotonic;
189afaa7b5aSDmitry Safonov offset[CLOCK_MONOTONIC_RAW] = monotonic;
190afaa7b5aSDmitry Safonov offset[CLOCK_MONOTONIC_COARSE] = monotonic;
191afaa7b5aSDmitry Safonov offset[CLOCK_BOOTTIME] = boottime;
192afaa7b5aSDmitry Safonov offset[CLOCK_BOOTTIME_ALARM] = boottime;
193afaa7b5aSDmitry Safonov }
194afaa7b5aSDmitry Safonov
find_timens_vvar_page(struct vm_area_struct * vma)195d6c494e8SJann Horn struct page *find_timens_vvar_page(struct vm_area_struct *vma)
196d6c494e8SJann Horn {
197d6c494e8SJann Horn if (likely(vma->vm_mm == current->mm))
198d6c494e8SJann Horn return current->nsproxy->time_ns->vvar_page;
199d6c494e8SJann Horn
200d6c494e8SJann Horn /*
201d6c494e8SJann Horn * VM_PFNMAP | VM_IO protect .fault() handler from being called
202d6c494e8SJann Horn * through interfaces like /proc/$pid/mem or
203d6c494e8SJann Horn * process_vm_{readv,writev}() as long as there's no .access()
204d6c494e8SJann Horn * in special_mapping_vmops().
205d6c494e8SJann Horn * For more details check_vma_flags() and __access_remote_vm()
206d6c494e8SJann Horn */
207d6c494e8SJann Horn
208d6c494e8SJann Horn WARN(1, "vvar_page accessed remotely");
209d6c494e8SJann Horn
210d6c494e8SJann Horn return NULL;
211d6c494e8SJann Horn }
212d6c494e8SJann Horn
213afaa7b5aSDmitry Safonov /*
214afaa7b5aSDmitry Safonov * Protects possibly multiple offsets writers racing each other
215afaa7b5aSDmitry Safonov * and tasks entering the namespace.
216afaa7b5aSDmitry Safonov */
217afaa7b5aSDmitry Safonov static DEFINE_MUTEX(offset_lock);
218afaa7b5aSDmitry Safonov
timens_set_vvar_page(struct task_struct * task,struct time_namespace * ns)219afaa7b5aSDmitry Safonov static void timens_set_vvar_page(struct task_struct *task,
220afaa7b5aSDmitry Safonov struct time_namespace *ns)
221afaa7b5aSDmitry Safonov {
222ac1a42f4SThomas Weißschuh struct vdso_time_data *vdata;
2235911e16cSAnna-Maria Behnsen struct vdso_clock *vc;
224afaa7b5aSDmitry Safonov unsigned int i;
225afaa7b5aSDmitry Safonov
226afaa7b5aSDmitry Safonov if (ns == &init_time_ns)
227afaa7b5aSDmitry Safonov return;
228afaa7b5aSDmitry Safonov
229afaa7b5aSDmitry Safonov /* Fast-path, taken by every task in namespace except the first. */
230afaa7b5aSDmitry Safonov if (likely(ns->frozen_offsets))
231afaa7b5aSDmitry Safonov return;
232afaa7b5aSDmitry Safonov
233afaa7b5aSDmitry Safonov mutex_lock(&offset_lock);
234afaa7b5aSDmitry Safonov /* Nothing to-do: vvar_page has been already initialized. */
235afaa7b5aSDmitry Safonov if (ns->frozen_offsets)
236afaa7b5aSDmitry Safonov goto out;
237afaa7b5aSDmitry Safonov
238afaa7b5aSDmitry Safonov ns->frozen_offsets = true;
239ac1a42f4SThomas Weißschuh vdata = page_address(ns->vvar_page);
240*886653e3SAnna-Maria Behnsen vc = vdata->clock_data;
241afaa7b5aSDmitry Safonov
242afaa7b5aSDmitry Safonov for (i = 0; i < CS_BASES; i++)
2435911e16cSAnna-Maria Behnsen timens_setup_vdso_clock_data(&vc[i], ns);
244afaa7b5aSDmitry Safonov
245afaa7b5aSDmitry Safonov out:
246afaa7b5aSDmitry Safonov mutex_unlock(&offset_lock);
247afaa7b5aSDmitry Safonov }
248afaa7b5aSDmitry Safonov
free_time_ns(struct time_namespace * ns)24928c41efdSKirill Tkhai void free_time_ns(struct time_namespace *ns)
250769071acSAndrei Vagin {
251769071acSAndrei Vagin dec_time_namespaces(ns->ucounts);
252769071acSAndrei Vagin put_user_ns(ns->user_ns);
253769071acSAndrei Vagin ns_free_inum(&ns->ns);
254afaa7b5aSDmitry Safonov __free_page(ns->vvar_page);
255769071acSAndrei Vagin kfree(ns);
256769071acSAndrei Vagin }
257769071acSAndrei Vagin
to_time_ns(struct ns_common * ns)258769071acSAndrei Vagin static struct time_namespace *to_time_ns(struct ns_common *ns)
259769071acSAndrei Vagin {
260769071acSAndrei Vagin return container_of(ns, struct time_namespace, ns);
261769071acSAndrei Vagin }
262769071acSAndrei Vagin
timens_get(struct task_struct * task)263769071acSAndrei Vagin static struct ns_common *timens_get(struct task_struct *task)
264769071acSAndrei Vagin {
265769071acSAndrei Vagin struct time_namespace *ns = NULL;
266769071acSAndrei Vagin struct nsproxy *nsproxy;
267769071acSAndrei Vagin
268769071acSAndrei Vagin task_lock(task);
269769071acSAndrei Vagin nsproxy = task->nsproxy;
270769071acSAndrei Vagin if (nsproxy) {
271769071acSAndrei Vagin ns = nsproxy->time_ns;
272769071acSAndrei Vagin get_time_ns(ns);
273769071acSAndrei Vagin }
274769071acSAndrei Vagin task_unlock(task);
275769071acSAndrei Vagin
276769071acSAndrei Vagin return ns ? &ns->ns : NULL;
277769071acSAndrei Vagin }
278769071acSAndrei Vagin
timens_for_children_get(struct task_struct * task)279769071acSAndrei Vagin static struct ns_common *timens_for_children_get(struct task_struct *task)
280769071acSAndrei Vagin {
281769071acSAndrei Vagin struct time_namespace *ns = NULL;
282769071acSAndrei Vagin struct nsproxy *nsproxy;
283769071acSAndrei Vagin
284769071acSAndrei Vagin task_lock(task);
285769071acSAndrei Vagin nsproxy = task->nsproxy;
286769071acSAndrei Vagin if (nsproxy) {
287769071acSAndrei Vagin ns = nsproxy->time_ns_for_children;
288769071acSAndrei Vagin get_time_ns(ns);
289769071acSAndrei Vagin }
290769071acSAndrei Vagin task_unlock(task);
291769071acSAndrei Vagin
292769071acSAndrei Vagin return ns ? &ns->ns : NULL;
293769071acSAndrei Vagin }
294769071acSAndrei Vagin
timens_put(struct ns_common * ns)295769071acSAndrei Vagin static void timens_put(struct ns_common *ns)
296769071acSAndrei Vagin {
297769071acSAndrei Vagin put_time_ns(to_time_ns(ns));
298769071acSAndrei Vagin }
299769071acSAndrei Vagin
timens_commit(struct task_struct * tsk,struct time_namespace * ns)30076c12881SChristian Brauner void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
3015cfea9a1SChristian Brauner {
3025cfea9a1SChristian Brauner timens_set_vvar_page(tsk, ns);
3035cfea9a1SChristian Brauner vdso_join_timens(tsk, ns);
3045cfea9a1SChristian Brauner }
3055cfea9a1SChristian Brauner
timens_install(struct nsset * nsset,struct ns_common * new)306f2a8d52eSChristian Brauner static int timens_install(struct nsset *nsset, struct ns_common *new)
307769071acSAndrei Vagin {
308f2a8d52eSChristian Brauner struct nsproxy *nsproxy = nsset->nsproxy;
309769071acSAndrei Vagin struct time_namespace *ns = to_time_ns(new);
310769071acSAndrei Vagin
311769071acSAndrei Vagin if (!current_is_single_threaded())
312769071acSAndrei Vagin return -EUSERS;
313769071acSAndrei Vagin
314769071acSAndrei Vagin if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
315f2a8d52eSChristian Brauner !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
316769071acSAndrei Vagin return -EPERM;
317769071acSAndrei Vagin
318769071acSAndrei Vagin get_time_ns(ns);
319769071acSAndrei Vagin put_time_ns(nsproxy->time_ns);
320769071acSAndrei Vagin nsproxy->time_ns = ns;
321769071acSAndrei Vagin
322769071acSAndrei Vagin get_time_ns(ns);
323769071acSAndrei Vagin put_time_ns(nsproxy->time_ns_for_children);
324769071acSAndrei Vagin nsproxy->time_ns_for_children = ns;
325769071acSAndrei Vagin return 0;
326769071acSAndrei Vagin }
327769071acSAndrei Vagin
timens_on_fork(struct nsproxy * nsproxy,struct task_struct * tsk)3285c62634fSHui Su void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
329769071acSAndrei Vagin {
330769071acSAndrei Vagin struct ns_common *nsc = &nsproxy->time_ns_for_children->ns;
331769071acSAndrei Vagin struct time_namespace *ns = to_time_ns(nsc);
332769071acSAndrei Vagin
333769071acSAndrei Vagin /* create_new_namespaces() already incremented the ref counter */
334769071acSAndrei Vagin if (nsproxy->time_ns == nsproxy->time_ns_for_children)
3355c62634fSHui Su return;
336769071acSAndrei Vagin
337769071acSAndrei Vagin get_time_ns(ns);
338769071acSAndrei Vagin put_time_ns(nsproxy->time_ns);
339769071acSAndrei Vagin nsproxy->time_ns = ns;
340769071acSAndrei Vagin
3415cfea9a1SChristian Brauner timens_commit(tsk, ns);
342769071acSAndrei Vagin }
343769071acSAndrei Vagin
timens_owner(struct ns_common * ns)344769071acSAndrei Vagin static struct user_namespace *timens_owner(struct ns_common *ns)
345769071acSAndrei Vagin {
346769071acSAndrei Vagin return to_time_ns(ns)->user_ns;
347769071acSAndrei Vagin }
348769071acSAndrei Vagin
show_offset(struct seq_file * m,int clockid,struct timespec64 * ts)34904a8682aSAndrei Vagin static void show_offset(struct seq_file *m, int clockid, struct timespec64 *ts)
35004a8682aSAndrei Vagin {
35194d440d6SAndrei Vagin char *clock;
35294d440d6SAndrei Vagin
35394d440d6SAndrei Vagin switch (clockid) {
35494d440d6SAndrei Vagin case CLOCK_BOOTTIME:
35594d440d6SAndrei Vagin clock = "boottime";
35694d440d6SAndrei Vagin break;
35794d440d6SAndrei Vagin case CLOCK_MONOTONIC:
35894d440d6SAndrei Vagin clock = "monotonic";
35994d440d6SAndrei Vagin break;
36094d440d6SAndrei Vagin default:
36194d440d6SAndrei Vagin clock = "unknown";
36294d440d6SAndrei Vagin break;
36394d440d6SAndrei Vagin }
36494d440d6SAndrei Vagin seq_printf(m, "%-10s %10lld %9ld\n", clock, ts->tv_sec, ts->tv_nsec);
36504a8682aSAndrei Vagin }
36604a8682aSAndrei Vagin
proc_timens_show_offsets(struct task_struct * p,struct seq_file * m)36704a8682aSAndrei Vagin void proc_timens_show_offsets(struct task_struct *p, struct seq_file *m)
36804a8682aSAndrei Vagin {
36904a8682aSAndrei Vagin struct ns_common *ns;
37004a8682aSAndrei Vagin struct time_namespace *time_ns;
37104a8682aSAndrei Vagin
37204a8682aSAndrei Vagin ns = timens_for_children_get(p);
37304a8682aSAndrei Vagin if (!ns)
37404a8682aSAndrei Vagin return;
37504a8682aSAndrei Vagin time_ns = to_time_ns(ns);
37604a8682aSAndrei Vagin
37704a8682aSAndrei Vagin show_offset(m, CLOCK_MONOTONIC, &time_ns->offsets.monotonic);
37804a8682aSAndrei Vagin show_offset(m, CLOCK_BOOTTIME, &time_ns->offsets.boottime);
37904a8682aSAndrei Vagin put_time_ns(time_ns);
38004a8682aSAndrei Vagin }
38104a8682aSAndrei Vagin
proc_timens_set_offset(struct file * file,struct task_struct * p,struct proc_timens_offset * offsets,int noffsets)38204a8682aSAndrei Vagin int proc_timens_set_offset(struct file *file, struct task_struct *p,
38304a8682aSAndrei Vagin struct proc_timens_offset *offsets, int noffsets)
38404a8682aSAndrei Vagin {
38504a8682aSAndrei Vagin struct ns_common *ns;
38604a8682aSAndrei Vagin struct time_namespace *time_ns;
38704a8682aSAndrei Vagin struct timespec64 tp;
38804a8682aSAndrei Vagin int i, err;
38904a8682aSAndrei Vagin
39004a8682aSAndrei Vagin ns = timens_for_children_get(p);
39104a8682aSAndrei Vagin if (!ns)
39204a8682aSAndrei Vagin return -ESRCH;
39304a8682aSAndrei Vagin time_ns = to_time_ns(ns);
39404a8682aSAndrei Vagin
39504a8682aSAndrei Vagin if (!file_ns_capable(file, time_ns->user_ns, CAP_SYS_TIME)) {
39604a8682aSAndrei Vagin put_time_ns(time_ns);
39704a8682aSAndrei Vagin return -EPERM;
39804a8682aSAndrei Vagin }
39904a8682aSAndrei Vagin
40004a8682aSAndrei Vagin for (i = 0; i < noffsets; i++) {
40104a8682aSAndrei Vagin struct proc_timens_offset *off = &offsets[i];
40204a8682aSAndrei Vagin
40304a8682aSAndrei Vagin switch (off->clockid) {
40404a8682aSAndrei Vagin case CLOCK_MONOTONIC:
40504a8682aSAndrei Vagin ktime_get_ts64(&tp);
40604a8682aSAndrei Vagin break;
40704a8682aSAndrei Vagin case CLOCK_BOOTTIME:
40804a8682aSAndrei Vagin ktime_get_boottime_ts64(&tp);
40904a8682aSAndrei Vagin break;
41004a8682aSAndrei Vagin default:
41104a8682aSAndrei Vagin err = -EINVAL;
41204a8682aSAndrei Vagin goto out;
41304a8682aSAndrei Vagin }
41404a8682aSAndrei Vagin
41504a8682aSAndrei Vagin err = -ERANGE;
41604a8682aSAndrei Vagin
41704a8682aSAndrei Vagin if (off->val.tv_sec > KTIME_SEC_MAX ||
41804a8682aSAndrei Vagin off->val.tv_sec < -KTIME_SEC_MAX)
41904a8682aSAndrei Vagin goto out;
42004a8682aSAndrei Vagin
42104a8682aSAndrei Vagin tp = timespec64_add(tp, off->val);
42204a8682aSAndrei Vagin /*
42304a8682aSAndrei Vagin * KTIME_SEC_MAX is divided by 2 to be sure that KTIME_MAX is
42404a8682aSAndrei Vagin * still unreachable.
42504a8682aSAndrei Vagin */
42604a8682aSAndrei Vagin if (tp.tv_sec < 0 || tp.tv_sec > KTIME_SEC_MAX / 2)
42704a8682aSAndrei Vagin goto out;
42804a8682aSAndrei Vagin }
42904a8682aSAndrei Vagin
43004a8682aSAndrei Vagin mutex_lock(&offset_lock);
43104a8682aSAndrei Vagin if (time_ns->frozen_offsets) {
43204a8682aSAndrei Vagin err = -EACCES;
43304a8682aSAndrei Vagin goto out_unlock;
43404a8682aSAndrei Vagin }
43504a8682aSAndrei Vagin
43604a8682aSAndrei Vagin err = 0;
43704a8682aSAndrei Vagin /* Don't report errors after this line */
43804a8682aSAndrei Vagin for (i = 0; i < noffsets; i++) {
43904a8682aSAndrei Vagin struct proc_timens_offset *off = &offsets[i];
44004a8682aSAndrei Vagin struct timespec64 *offset = NULL;
44104a8682aSAndrei Vagin
44204a8682aSAndrei Vagin switch (off->clockid) {
44304a8682aSAndrei Vagin case CLOCK_MONOTONIC:
44404a8682aSAndrei Vagin offset = &time_ns->offsets.monotonic;
44504a8682aSAndrei Vagin break;
44604a8682aSAndrei Vagin case CLOCK_BOOTTIME:
44704a8682aSAndrei Vagin offset = &time_ns->offsets.boottime;
44804a8682aSAndrei Vagin break;
44904a8682aSAndrei Vagin }
45004a8682aSAndrei Vagin
45104a8682aSAndrei Vagin *offset = off->val;
45204a8682aSAndrei Vagin }
45304a8682aSAndrei Vagin
45404a8682aSAndrei Vagin out_unlock:
45504a8682aSAndrei Vagin mutex_unlock(&offset_lock);
45604a8682aSAndrei Vagin out:
45704a8682aSAndrei Vagin put_time_ns(time_ns);
45804a8682aSAndrei Vagin
45904a8682aSAndrei Vagin return err;
46004a8682aSAndrei Vagin }
46104a8682aSAndrei Vagin
462769071acSAndrei Vagin const struct proc_ns_operations timens_operations = {
463769071acSAndrei Vagin .name = "time",
464769071acSAndrei Vagin .type = CLONE_NEWTIME,
465769071acSAndrei Vagin .get = timens_get,
466769071acSAndrei Vagin .put = timens_put,
467769071acSAndrei Vagin .install = timens_install,
468769071acSAndrei Vagin .owner = timens_owner,
469769071acSAndrei Vagin };
470769071acSAndrei Vagin
471769071acSAndrei Vagin const struct proc_ns_operations timens_for_children_operations = {
472769071acSAndrei Vagin .name = "time_for_children",
473b801f1e2SMichael Kerrisk (man-pages) .real_ns_name = "time",
474769071acSAndrei Vagin .type = CLONE_NEWTIME,
475769071acSAndrei Vagin .get = timens_for_children_get,
476769071acSAndrei Vagin .put = timens_put,
477769071acSAndrei Vagin .install = timens_install,
478769071acSAndrei Vagin .owner = timens_owner,
479769071acSAndrei Vagin };
480769071acSAndrei Vagin
481769071acSAndrei Vagin struct time_namespace init_time_ns = {
48228c41efdSKirill Tkhai .ns.count = REFCOUNT_INIT(3),
483769071acSAndrei Vagin .user_ns = &init_user_ns,
484769071acSAndrei Vagin .ns.inum = PROC_TIME_INIT_INO,
485769071acSAndrei Vagin .ns.ops = &timens_operations,
486afaa7b5aSDmitry Safonov .frozen_offsets = true,
487769071acSAndrei Vagin };
488