lkml.org 
[lkml]   [2019]   [Nov]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCHv8 26/34] x86/vdso: Zap vvar pages on switch a time namspace
    Date
    The VVAR page layout depends on whether a task belongs to the root or
    non-root time namespace. Whenever a task changes its namespace, the VVAR
    page tables are cleared and then they will re-faulted with a
    corresponding layout.

    Co-developed-by: Andrei Vagin <avagin@gmail.com>
    Signed-off-by: Andrei Vagin <avagin@gmail.com>
    Signed-off-by: Dmitry Safonov <dima@arista.com>
    ---
    arch/x86/entry/vdso/vma.c | 27 +++++++++++++++++++++++++++
    include/linux/time_namespace.h | 9 +++++++++
    kernel/time/namespace.c | 10 ++++++++++
    3 files changed, 46 insertions(+)

    diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
    index d6cb8a16f368..57ada3e95f8d 100644
    --- a/arch/x86/entry/vdso/vma.c
    +++ b/arch/x86/entry/vdso/vma.c
    @@ -50,6 +50,7 @@ void __init init_vdso_image(const struct vdso_image *image)
    image->alt_len));
    }

    +static const struct vm_special_mapping vvar_mapping;
    struct linux_binprm;

    static vm_fault_t vdso_fault(const struct vm_special_mapping *sm,
    @@ -127,6 +128,32 @@ static struct page *find_timens_vvar_page(struct vm_area_struct *vma)

    return NULL;
    }
    +
    +/*
    + * The vvar page layout depends on whether a task belongs to the root or
    + * non-root time namespace. Whenever a task changes its namespace, the VVAR
    + * page tables are cleared and then they will re-faulted with a
    + * corresponding layout.
    + * See also the comment near timens_setup_vdso_data() for details.
    + */
    +int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
    +{
    + struct mm_struct *mm = task->mm;
    + struct vm_area_struct *vma;
    +
    + if (down_write_killable(&mm->mmap_sem))
    + return -EINTR;
    +
    + for (vma = mm->mmap; vma; vma = vma->vm_next) {
    + unsigned long size = vma->vm_end - vma->vm_start;
    +
    + if (vma_is_special_mapping(vma, &vvar_mapping))
    + zap_page_range(vma, vma->vm_start, size);
    + }
    +
    + up_write(&mm->mmap_sem);
    + return 0;
    +}
    #else
    static inline struct page *find_timens_vvar_page(struct vm_area_struct *vma)
    {
    diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
    index cdb438ba0037..f1cdd3a6f842 100644
    --- a/include/linux/time_namespace.h
    +++ b/include/linux/time_namespace.h
    @@ -30,6 +30,9 @@ struct time_namespace {
    extern struct time_namespace init_time_ns;

    #ifdef CONFIG_TIME_NS
    +extern int vdso_join_timens(struct task_struct *task,
    + struct time_namespace *ns);
    +
    static inline struct time_namespace *get_time_ns(struct time_namespace *ns)
    {
    kref_get(&ns->kref);
    @@ -74,6 +77,12 @@ static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim)
    }

    #else
    +static inline int vdso_join_timens(struct task_struct *task,
    + struct time_namespace *ns)
    +{
    + return 0;
    +}
    +
    static inline struct time_namespace *get_time_ns(struct time_namespace *ns)
    {
    return NULL;
    diff --git a/kernel/time/namespace.c b/kernel/time/namespace.c
    index e14cd1ca387d..0dc0742ed1ee 100644
    --- a/kernel/time/namespace.c
    +++ b/kernel/time/namespace.c
    @@ -280,6 +280,7 @@ static void timens_put(struct ns_common *ns)
    static int timens_install(struct nsproxy *nsproxy, struct ns_common *new)
    {
    struct time_namespace *ns = to_time_ns(new);
    + int err;

    if (!current_is_single_threaded())
    return -EUSERS;
    @@ -290,6 +291,10 @@ static int timens_install(struct nsproxy *nsproxy, struct ns_common *new)

    timens_set_vvar_page(current, ns);

    + err = vdso_join_timens(current, ns);
    + if (err)
    + return err;
    +
    get_time_ns(ns);
    put_time_ns(nsproxy->time_ns);
    nsproxy->time_ns = ns;
    @@ -304,6 +309,7 @@ int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
    {
    struct ns_common *nsc = &nsproxy->time_ns_for_children->ns;
    struct time_namespace *ns = to_time_ns(nsc);
    + int err;

    /* create_new_namespaces() already incremented the ref counter */
    if (nsproxy->time_ns == nsproxy->time_ns_for_children)
    @@ -311,6 +317,10 @@ int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)

    timens_set_vvar_page(tsk, ns);

    + err = vdso_join_timens(tsk, ns);
    + if (err)
    + return err;
    +
    get_time_ns(ns);
    put_time_ns(nsproxy->time_ns);
    nsproxy->time_ns = ns;
    --
    2.24.0
    \
     
     \ /
      Last update: 2019-11-12 02:29    [W:4.190 / U:0.512 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site