x86/vdso: Handle faults on timens page
authorDmitry Safonov <dima@arista.com>
Tue, 12 Nov 2019 01:27:13 +0000 (01:27 +0000)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 14 Jan 2020 11:20:58 +0000 (12:20 +0100)
If a task belongs to a time namespace then the VVAR page which contains
the system wide VDSO data is replaced with a namespace specific page
which has the same layout as the VVAR page.

Co-developed-by: Andrei Vagin <avagin@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20191112012724.250792-25-dima@arista.com
arch/x86/entry/vdso/vma.c
mm/mmap.c

index 04e3498..e5f3361 100644 (file)
 #include <linux/elf.h>
 #include <linux/cpu.h>
 #include <linux/ptrace.h>
+#include <linux/time_namespace.h>
+
 #include <asm/pvclock.h>
 #include <asm/vgtod.h>
 #include <asm/proto.h>
 #include <asm/vdso.h>
 #include <asm/vvar.h>
+#include <asm/tlb.h>
 #include <asm/page.h>
 #include <asm/desc.h>
 #include <asm/cpufeature.h>
@@ -107,10 +110,36 @@ static int vvar_mremap(const struct vm_special_mapping *sm,
        return 0;
 }
 
+#ifdef CONFIG_TIME_NS
+static struct page *find_timens_vvar_page(struct vm_area_struct *vma)
+{
+       if (likely(vma->vm_mm == current->mm))
+               return current->nsproxy->time_ns->vvar_page;
+
+       /*
+        * VM_PFNMAP | VM_IO protect .fault() handler from being called
+        * through interfaces like /proc/$pid/mem or
+        * process_vm_{readv,writev}() as long as there's no .access()
+        * in special_mapping_vmops().
+        * For more details check_vma_flags() and __access_remote_vm()
+        */
+
+       WARN(1, "vvar_page accessed remotely");
+
+       return NULL;
+}
+#else
+static inline struct page *find_timens_vvar_page(struct vm_area_struct *vma)
+{
+       return NULL;
+}
+#endif
+
 static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
                      struct vm_area_struct *vma, struct vm_fault *vmf)
 {
        const struct vdso_image *image = vma->vm_mm->context.vdso_image;
+       unsigned long pfn;
        long sym_offset;
 
        if (!image)
@@ -130,8 +159,21 @@ static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
                return VM_FAULT_SIGBUS;
 
        if (sym_offset == image->sym_vvar_page) {
-               return vmf_insert_pfn(vma, vmf->address,
-                               __pa_symbol(&__vvar_page) >> PAGE_SHIFT);
+               struct page *timens_page = find_timens_vvar_page(vma);
+
+               pfn = __pa_symbol(&__vvar_page) >> PAGE_SHIFT;
+
+               /*
+                * If a task belongs to a time namespace then a namespace
+                * specific VVAR is mapped with the sym_vvar_page offset and
+                * the real VVAR page is mapped with the sym_timens_page
+                * offset.
+                * See also the comment near timens_setup_vdso_data().
+                */
+               if (timens_page)
+                       pfn = page_to_pfn(timens_page);
+
+               return vmf_insert_pfn(vma, vmf->address, pfn);
        } else if (sym_offset == image->sym_pvclock_page) {
                struct pvclock_vsyscall_time_info *pvti =
                        pvclock_get_pvti_cpu0_va();
@@ -146,6 +188,14 @@ static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
                if (tsc_pg && vclock_was_used(VCLOCK_HVCLOCK))
                        return vmf_insert_pfn(vma, vmf->address,
                                        virt_to_phys(tsc_pg) >> PAGE_SHIFT);
+       } else if (sym_offset == image->sym_timens_page) {
+               struct page *timens_page = find_timens_vvar_page(vma);
+
+               if (!timens_page)
+                       return VM_FAULT_SIGBUS;
+
+               pfn = __pa_symbol(&__vvar_page) >> PAGE_SHIFT;
+               return vmf_insert_pfn(vma, vmf->address, pfn);
        }
 
        return VM_FAULT_SIGBUS;
index 9c64852..60c17d3 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -3342,6 +3342,8 @@ static const struct vm_operations_struct special_mapping_vmops = {
        .fault = special_mapping_fault,
        .mremap = special_mapping_mremap,
        .name = special_mapping_name,
+       /* vDSO code relies that VVAR can't be accessed remotely */
+       .access = NULL,
 };
 
 static const struct vm_operations_struct legacy_special_mapping_vmops = {