x86/vdso: Zap vvar pages when switching to a time namespace
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 be 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> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/r/20191112012724.250792-27-dima@arista.com
This commit is contained in:
		
							parent
							
								
									e6b28ec65b
								
							
						
					
					
						commit
						70ddf65184
					
				| @ -51,6 +51,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, | ||||
| @ -128,6 +129,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) | ||||
| { | ||||
|  | ||||
| @ -31,6 +31,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); | ||||
| @ -77,6 +80,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; | ||||
|  | ||||
| @ -281,6 +281,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; | ||||
| @ -291,6 +292,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; | ||||
| @ -305,6 +310,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) | ||||
| @ -312,6 +318,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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user