Christian Brauner (Amutable)exec_state: relocate dumpable information6b1c66c5 months agoHistory
3074 lines · 72.8 KB · C
RawDownload
	int error = -EINVAL, i; 	static const unsigned char offsets[] = {		offsetof(struct prctl_mm_map, start_code),		offsetof(struct prctl_mm_map, end_code),		offsetof(struct prctl_mm_map, start_data),		offsetof(struct prctl_mm_map, end_data),		offsetof(struct prctl_mm_map, start_brk),		offsetof(struct prctl_mm_map, brk),		offsetof(struct prctl_mm_map, start_stack),		offsetof(struct prctl_mm_map, arg_start),		offsetof(struct prctl_mm_map, arg_end),		offsetof(struct prctl_mm_map, env_start),		offsetof(struct prctl_mm_map, env_end),	}; 	/*	 * Make sure the members are not somewhere outside	 * of allowed address space.	 */	for (i = 0; i < ARRAY_SIZE(offsets); i++) {		u64 val = *(u64 *)((char *)prctl_map + offsets[i]); 		if ((unsigned long)val >= mmap_max_addr ||		    (unsigned long)val < mmap_min_addr)			goto out;	} 	/*	 * Make sure the pairs are ordered.	 */#define __prctl_check_order(__m1, __op, __m2)				\	((unsigned long)prctl_map->__m1 __op				\	 (unsigned long)prctl_map->__m2) ? 0 : -EINVAL	error  = __prctl_check_order(start_code, <, end_code);	error |= __prctl_check_order(start_data,<=, end_data);	error |= __prctl_check_order(start_brk, <=, brk);	error |= __prctl_check_order(arg_start, <=, arg_end);	error |= __prctl_check_order(env_start, <=, env_end);	if (error)		goto out;#undef __prctl_check_order 	error = -EINVAL; 	/*	 * Neither we should allow to override limits if they set.	 */	if (check_data_rlimit(rlimit(RLIMIT_DATA), prctl_map->brk,			      prctl_map->start_brk, prctl_map->end_data,			      prctl_map->start_data))			goto out; 	error = 0;out:	return error;} #ifdef CONFIG_CHECKPOINT_RESTOREstatic int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data_size){	struct prctl_mm_map prctl_map = { .exe_fd = (u32)-1, };	unsigned long user_auxv[AT_VECTOR_SIZE];	struct mm_struct *mm = current->mm;	int error; 	BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv));	BUILD_BUG_ON(sizeof(struct prctl_mm_map) > 256); 	if (opt == PR_SET_MM_MAP_SIZE)		return put_user((unsigned int)sizeof(prctl_map),				(unsigned int __user *)addr); 	if (data_size != sizeof(prctl_map))		return -EINVAL; 	if (copy_from_user(&prctl_map, addr, sizeof(prctl_map)))		return -EFAULT; 	error = validate_prctl_map_addr(&prctl_map);	if (error)		return error; 	if (prctl_map.auxv_size) {		/*		 * Someone is trying to cheat the auxv vector.		 */		if (!prctl_map.auxv ||				prctl_map.auxv_size > sizeof(mm->saved_auxv))			return -EINVAL; 		memset(user_auxv, 0, sizeof(user_auxv));		if (copy_from_user(user_auxv,				   (const void __user *)prctl_map.auxv,				   prctl_map.auxv_size))			return -EFAULT; 		/* Last entry must be AT_NULL as specification requires */		user_auxv[AT_VECTOR_SIZE - 2] = AT_NULL;		user_auxv[AT_VECTOR_SIZE - 1] = AT_NULL;	} 	if (prctl_map.exe_fd != (u32)-1) {		/*		 * Check if the current user is checkpoint/restore capable.		 * At the time of this writing, it checks for CAP_SYS_ADMIN		 * or CAP_CHECKPOINT_RESTORE.		 * Note that a user with access to ptrace can masquerade an		 * arbitrary program as any executable, even setuid ones.		 * This may have implications in the tomoyo subsystem.		 */		if (!checkpoint_restore_ns_capable(current_user_ns()))			return -EPERM; 		error = prctl_set_mm_exe_file(mm, prctl_map.exe_fd);		if (error)			return error;	} 	/*	 * arg_lock protects concurrent updates but we still need mmap_lock for	 * read to exclude races with sys_brk.	 */	mmap_read_lock(mm); 	/*	 * We don't validate if these members are pointing to	 * real present VMAs because application may have correspond	 * VMAs already unmapped and kernel uses these members for statistics	 * output in procfs mostly, except	 *	 *  - @start_brk/@brk which are used in do_brk_flags but kernel lookups	 *    for VMAs when updating these members so anything wrong written	 *    here cause kernel to swear at userspace program but won't lead	 *    to any problem in kernel itself	 */ 	spin_lock(&mm->arg_lock);	mm->start_code	= prctl_map.start_code;	mm->end_code	= prctl_map.end_code;	mm->start_data	= prctl_map.start_data;	mm->end_data	= prctl_map.end_data;	mm->start_brk	= prctl_map.start_brk;	mm->brk		= prctl_map.brk;	mm->start_stack	= prctl_map.start_stack;	mm->arg_start	= prctl_map.arg_start;	mm->arg_end	= prctl_map.arg_end;	mm->env_start	= prctl_map.env_start;	mm->env_end	= prctl_map.env_end;	spin_unlock(&mm->arg_lock); 	/*	 * Note this update of @saved_auxv is lockless thus	 * if someone reads this member in procfs while we're	 * updating -- it may get partly updated results. It's	 * known and acceptable trade off: we leave it as is to	 * not introduce additional locks here making the kernel	 * more complex.	 */	if (prctl_map.auxv_size)		memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv)); 	mmap_read_unlock(mm);	return 0;}#endif /* CONFIG_CHECKPOINT_RESTORE */ static int prctl_set_auxv(struct mm_struct *mm, unsigned long addr,			  unsigned long len){	/*	 * This doesn't move the auxiliary vector itself since it's pinned to	 * mm_struct, but it permits filling the vector with new values.  It's	 * up to the caller to provide sane values here, otherwise userspace	 * tools which use this vector might be unhappy.	 */	unsigned long user_auxv[AT_VECTOR_SIZE] = {}; 	if (len > sizeof(user_auxv))		return -EINVAL; 	if (copy_from_user(user_auxv, (const void __user *)addr, len))		return -EFAULT; 	/* Make sure the last entry is always AT_NULL */	user_auxv[AT_VECTOR_SIZE - 2] = 0;	user_auxv[AT_VECTOR_SIZE - 1] = 0; 	BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv)); 	task_lock(current);	memcpy(mm->saved_auxv, user_auxv, len);	task_unlock(current); 	return 0;} static int prctl_set_mm(int opt, unsigned long addr,			unsigned long arg4, unsigned long arg5){	struct mm_struct *mm = current->mm;	struct prctl_mm_map prctl_map = {		.auxv = NULL,		.auxv_size = 0,		.exe_fd = -1,	};	struct vm_area_struct *vma;	int error; 	if (arg5 || (arg4 && (opt != PR_SET_MM_AUXV &&			      opt != PR_SET_MM_MAP &&			      opt != PR_SET_MM_MAP_SIZE)))		return -EINVAL; #ifdef CONFIG_CHECKPOINT_RESTORE	if (opt == PR_SET_MM_MAP || opt == PR_SET_MM_MAP_SIZE)		return prctl_set_mm_map(opt, (const void __user *)addr, arg4);#endif 	if (!capable(CAP_SYS_RESOURCE))		return -EPERM; 	if (opt == PR_SET_MM_EXE_FILE)		return prctl_set_mm_exe_file(mm, (unsigned int)addr); 	if (opt == PR_SET_MM_AUXV)		return prctl_set_auxv(mm, addr, arg4); 	if (addr >= TASK_SIZE || addr < mmap_min_addr)		return -EINVAL; 	error = -EINVAL; 	/*	 * arg_lock protects concurrent updates of arg boundaries, we need	 * mmap_lock for a) concurrent sys_brk, b) finding VMA for addr	 * validation.	 */	mmap_read_lock(mm);	vma = find_vma(mm, addr); 	spin_lock(&mm->arg_lock);	prctl_map.start_code	= mm->start_code;	prctl_map.end_code	= mm->end_code;	prctl_map.start_data	= mm->start_data;	prctl_map.end_data	= mm->end_data;	prctl_map.start_brk	= mm->start_brk;	prctl_map.brk		= mm->brk;	prctl_map.start_stack	= mm->start_stack;	prctl_map.arg_start	= mm->arg_start;	prctl_map.arg_end	= mm->arg_end;	prctl_map.env_start	= mm->env_start;	prctl_map.env_end	= mm->env_end; 	switch (opt) {	case PR_SET_MM_START_CODE:		prctl_map.start_code = addr;		break;	case PR_SET_MM_END_CODE:		prctl_map.end_code = addr;		break;	case PR_SET_MM_START_DATA:		prctl_map.start_data = addr;		break;	case PR_SET_MM_END_DATA:		prctl_map.end_data = addr;		break;	case PR_SET_MM_START_STACK:		prctl_map.start_stack = addr;		break;	case PR_SET_MM_START_BRK:		prctl_map.start_brk = addr;		break;	case PR_SET_MM_BRK:		prctl_map.brk = addr;		break;	case PR_SET_MM_ARG_START:		prctl_map.arg_start = addr;		break;	case PR_SET_MM_ARG_END:		prctl_map.arg_end = addr;		break;	case PR_SET_MM_ENV_START:		prctl_map.env_start = addr;		break;	case PR_SET_MM_ENV_END:		prctl_map.env_end = addr;		break;	default:		goto out;	} 	error = validate_prctl_map_addr(&prctl_map);	if (error)		goto out; 	switch (opt) {	/*	 * If command line arguments and environment	 * are placed somewhere else on stack, we can	 * set them up here, ARG_START/END to setup	 * command line arguments and ENV_START/END	 * for environment.	 */	case PR_SET_MM_START_STACK:	case PR_SET_MM_ARG_START:	case PR_SET_MM_ARG_END:	case PR_SET_MM_ENV_START:	case PR_SET_MM_ENV_END:		if (!vma) {			error = -EFAULT;			goto out;		}	} 	mm->start_code	= prctl_map.start_code;	mm->end_code	= prctl_map.end_code;	mm->start_data	= prctl_map.start_data;	mm->end_data	= prctl_map.end_data;	mm->start_brk	= prctl_map.start_brk;	mm->brk		= prctl_map.brk;	mm->start_stack	= prctl_map.start_stack;	mm->arg_start	= prctl_map.arg_start;	mm->arg_end	= prctl_map.arg_end;	mm->env_start	= prctl_map.env_start;	mm->env_end	= prctl_map.env_end; 	error = 0;out:	spin_unlock(&mm->arg_lock);	mmap_read_unlock(mm);	return error;} #ifdef CONFIG_CHECKPOINT_RESTOREstatic int prctl_get_tid_address(struct task_struct *me, int __user * __user *tid_addr){	return put_user(me->clear_child_tid, tid_addr);}#elsestatic int prctl_get_tid_address(struct task_struct *me, int __user * __user *tid_addr){	return -EINVAL;}#endif static int propagate_has_child_subreaper(struct task_struct *p, void *data){	/*	 * If task has has_child_subreaper - all its descendants	 * already have these flag too and new descendants will	 * inherit it on fork, skip them.	 *	 * If we've found child_reaper - skip descendants in	 * it's subtree as they will never get out pidns.	 */	if (p->signal->has_child_subreaper ||	    is_child_reaper(task_pid(p)))		return 0; 	p->signal->has_child_subreaper = 1;	return 1;} int __weak arch_prctl_spec_ctrl_get(struct task_struct *t, unsigned long which){	return -EINVAL;} int __weak arch_prctl_spec_ctrl_set(struct task_struct *t, unsigned long which,				    unsigned long ctrl){	return -EINVAL;} int __weak arch_get_shadow_stack_status(struct task_struct *t, unsigned long __user *status){	return -EINVAL;} int __weak arch_set_shadow_stack_status(struct task_struct *t, unsigned long status){	return -EINVAL;} int __weak arch_lock_shadow_stack_status(struct task_struct *t, unsigned long status){	return -EINVAL;} int __weak arch_prctl_get_branch_landing_pad_state(struct task_struct *t,						   unsigned long __user *state){	return -EINVAL;} int __weak arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state){	return -EINVAL;} int __weak arch_prctl_lock_branch_landing_pad_state(struct task_struct *t){	return -EINVAL;} #define PR_IO_FLUSHER (PF_MEMALLOC_NOIO | PF_LOCAL_THROTTLE) static int prctl_set_vma(unsigned long opt, unsigned long addr,			 unsigned long size, unsigned long arg){	int error; 	switch (opt) {	case PR_SET_VMA_ANON_NAME:		error = set_anon_vma_name(addr, size, (const char __user *)arg);		break;	default:		error = -EINVAL;	} 	return error;} static inline unsigned long get_current_mdwe(void){	unsigned long ret = 0; 	if (mm_flags_test(MMF_HAS_MDWE, current->mm))		ret |= PR_MDWE_REFUSE_EXEC_GAIN;	if (mm_flags_test(MMF_HAS_MDWE_NO_INHERIT, current->mm))		ret |= PR_MDWE_NO_INHERIT; 	return ret;} static inline int prctl_set_mdwe(unsigned long bits, unsigned long arg3,				 unsigned long arg4, unsigned long arg5){	unsigned long current_bits; 	if (arg3 || arg4 || arg5)		return -EINVAL; 	if (bits & ~(PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT))		return -EINVAL; 	/* NO_INHERIT only makes sense with REFUSE_EXEC_GAIN */	if (bits & PR_MDWE_NO_INHERIT && !(bits & PR_MDWE_REFUSE_EXEC_GAIN))		return -EINVAL; 	/*	 * EOPNOTSUPP might be more appropriate here in principle, but	 * existing userspace depends on EINVAL specifically.	 */	if (!arch_memory_deny_write_exec_supported())		return -EINVAL; 	current_bits = get_current_mdwe();	if (current_bits && current_bits != bits)		return -EPERM; /* Cannot unset the flags */ 	if (bits & PR_MDWE_NO_INHERIT)		mm_flags_set(MMF_HAS_MDWE_NO_INHERIT, current->mm);	if (bits & PR_MDWE_REFUSE_EXEC_GAIN)		mm_flags_set(MMF_HAS_MDWE, current->mm); 	return 0;} static inline int prctl_get_mdwe(unsigned long arg2, unsigned long arg3,				 unsigned long arg4, unsigned long arg5){	if (arg2 || arg3 || arg4 || arg5)		return -EINVAL;	return get_current_mdwe();} static int prctl_get_auxv(void __user *addr, unsigned long len){	struct mm_struct *mm = current->mm;	unsigned long size = min_t(unsigned long, sizeof(mm->saved_auxv), len); 	if (size && copy_to_user(addr, mm->saved_auxv, size))		return -EFAULT;	return sizeof(mm->saved_auxv);} static int prctl_get_thp_disable(unsigned long arg2, unsigned long arg3,				 unsigned long arg4, unsigned long arg5){	struct mm_struct *mm = current->mm; 	if (arg2 || arg3 || arg4 || arg5)		return -EINVAL; 	/* If disabled, we return "1 | flags", otherwise 0. */	if (mm_flags_test(MMF_DISABLE_THP_COMPLETELY, mm))		return 1;	else if (mm_flags_test(MMF_DISABLE_THP_EXCEPT_ADVISED, mm))

Lines 2001–2500 of 3074. Previous lines

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t