Christian Brauner (Amutable)exec_state: relocate dumpable information6b1c66c5 months agoHistory
3074 lines · 72.8 KB · C
RawDownload
		      struct rlimit *new_rlim, struct rlimit *old_rlim){	struct rlimit *rlim;	int retval = 0; 	if (resource >= RLIM_NLIMITS)		return -EINVAL;	resource = array_index_nospec(resource, RLIM_NLIMITS); 	if (new_rlim) {		if (new_rlim->rlim_cur > new_rlim->rlim_max)			return -EINVAL;		if (resource == RLIMIT_NOFILE &&				new_rlim->rlim_max > sysctl_nr_open)			return -EPERM;	} 	/* Holding a refcount on tsk protects tsk->signal from disappearing. */	rlim = tsk->signal->rlim + resource;	task_lock(tsk->group_leader);	if (new_rlim) {		/*		 * Keep the capable check against init_user_ns until cgroups can		 * contain all limits.		 */		if (new_rlim->rlim_max > rlim->rlim_max &&				!capable(CAP_SYS_RESOURCE))			retval = -EPERM;		if (!retval)			retval = security_task_setrlimit(tsk, resource, new_rlim);	}	if (!retval) {		if (old_rlim)			*old_rlim = *rlim;		if (new_rlim)			*rlim = *new_rlim;	}	task_unlock(tsk->group_leader); 	/*	 * RLIMIT_CPU handling. Arm the posix CPU timer if the limit is not	 * infinite. In case of RLIM_INFINITY the posix CPU timer code	 * ignores the rlimit.	 */	if (!retval && new_rlim && resource == RLIMIT_CPU &&	    new_rlim->rlim_cur != RLIM_INFINITY &&	    IS_ENABLED(CONFIG_POSIX_TIMERS)) {		/*		 * update_rlimit_cpu can fail if the task is exiting, but there		 * may be other tasks in the thread group that are not exiting,		 * and they need their cpu timers adjusted.		 *		 * The group_leader is the last task to be released, so if we		 * cannot update_rlimit_cpu on it, then the entire process is		 * exiting and we do not need to update at all.		 */		update_rlimit_cpu(tsk->group_leader, new_rlim->rlim_cur);	} 	return retval;} SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim){	struct rlimit value;	int ret; 	ret = do_prlimit(current, resource, NULL, &value);	if (!ret)		ret = copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0; 	return ret;} #ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE2(setrlimit, unsigned int, resource,		       struct compat_rlimit __user *, rlim){	struct rlimit r;	struct compat_rlimit r32; 	if (copy_from_user(&r32, rlim, sizeof(struct compat_rlimit)))		return -EFAULT; 	if (r32.rlim_cur == COMPAT_RLIM_INFINITY)		r.rlim_cur = RLIM_INFINITY;	else		r.rlim_cur = r32.rlim_cur;	if (r32.rlim_max == COMPAT_RLIM_INFINITY)		r.rlim_max = RLIM_INFINITY;	else		r.rlim_max = r32.rlim_max;	return do_prlimit(current, resource, &r, NULL);} COMPAT_SYSCALL_DEFINE2(getrlimit, unsigned int, resource,		       struct compat_rlimit __user *, rlim){	struct rlimit r;	int ret; 	ret = do_prlimit(current, resource, NULL, &r);	if (!ret) {		struct compat_rlimit r32;		if (r.rlim_cur > COMPAT_RLIM_INFINITY)			r32.rlim_cur = COMPAT_RLIM_INFINITY;		else			r32.rlim_cur = r.rlim_cur;		if (r.rlim_max > COMPAT_RLIM_INFINITY)			r32.rlim_max = COMPAT_RLIM_INFINITY;		else			r32.rlim_max = r.rlim_max; 		if (copy_to_user(rlim, &r32, sizeof(struct compat_rlimit)))			return -EFAULT;	}	return ret;} #endif #ifdef __ARCH_WANT_SYS_OLD_GETRLIMIT /* *	Back compatibility for getrlimit. Needed for some apps. */SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,		struct rlimit __user *, rlim){	struct rlimit x;	if (resource >= RLIM_NLIMITS)		return -EINVAL; 	resource = array_index_nospec(resource, RLIM_NLIMITS);	task_lock(current->group_leader);	x = current->signal->rlim[resource];	task_unlock(current->group_leader);	if (x.rlim_cur > 0x7FFFFFFF)		x.rlim_cur = 0x7FFFFFFF;	if (x.rlim_max > 0x7FFFFFFF)		x.rlim_max = 0x7FFFFFFF;	return copy_to_user(rlim, &x, sizeof(x)) ? -EFAULT : 0;} #ifdef CONFIG_COMPATCOMPAT_SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,		       struct compat_rlimit __user *, rlim){	struct rlimit r; 	if (resource >= RLIM_NLIMITS)		return -EINVAL; 	resource = array_index_nospec(resource, RLIM_NLIMITS);	task_lock(current->group_leader);	r = current->signal->rlim[resource];	task_unlock(current->group_leader);	if (r.rlim_cur > 0x7FFFFFFF)		r.rlim_cur = 0x7FFFFFFF;	if (r.rlim_max > 0x7FFFFFFF)		r.rlim_max = 0x7FFFFFFF; 	if (put_user(r.rlim_cur, &rlim->rlim_cur) ||	    put_user(r.rlim_max, &rlim->rlim_max))		return -EFAULT;	return 0;}#endif #endif static inline bool rlim64_is_infinity(__u64 rlim64){#if BITS_PER_LONG < 64	return rlim64 >= ULONG_MAX;#else	return rlim64 == RLIM64_INFINITY;#endif} static void rlim_to_rlim64(const struct rlimit *rlim, struct rlimit64 *rlim64){	if (rlim->rlim_cur == RLIM_INFINITY)		rlim64->rlim_cur = RLIM64_INFINITY;	else		rlim64->rlim_cur = rlim->rlim_cur;	if (rlim->rlim_max == RLIM_INFINITY)		rlim64->rlim_max = RLIM64_INFINITY;	else		rlim64->rlim_max = rlim->rlim_max;} static void rlim64_to_rlim(const struct rlimit64 *rlim64, struct rlimit *rlim){	if (rlim64_is_infinity(rlim64->rlim_cur))		rlim->rlim_cur = RLIM_INFINITY;	else		rlim->rlim_cur = (unsigned long)rlim64->rlim_cur;	if (rlim64_is_infinity(rlim64->rlim_max))		rlim->rlim_max = RLIM_INFINITY;	else		rlim->rlim_max = (unsigned long)rlim64->rlim_max;} /* rcu lock must be held */static int check_prlimit_permission(struct task_struct *task,				    unsigned int flags){	const struct cred *cred = current_cred(), *tcred;	bool id_match; 	if (current == task)		return 0; 	tcred = __task_cred(task);	id_match = (uid_eq(cred->uid, tcred->euid) &&		    uid_eq(cred->uid, tcred->suid) &&		    uid_eq(cred->uid, tcred->uid)  &&		    gid_eq(cred->gid, tcred->egid) &&		    gid_eq(cred->gid, tcred->sgid) &&		    gid_eq(cred->gid, tcred->gid));	if (!id_match && !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))		return -EPERM; 	return security_task_prlimit(cred, tcred, flags);} SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,		const struct rlimit64 __user *, new_rlim,		struct rlimit64 __user *, old_rlim){	struct rlimit64 old64, new64;	struct rlimit old, new;	struct task_struct *tsk;	unsigned int checkflags = 0;	bool need_tasklist;	int ret; 	if (old_rlim)		checkflags |= LSM_PRLIMIT_READ; 	if (new_rlim) {		if (copy_from_user(&new64, new_rlim, sizeof(new64)))			return -EFAULT;		rlim64_to_rlim(&new64, &new);		checkflags |= LSM_PRLIMIT_WRITE;	} 	rcu_read_lock();	tsk = pid ? find_task_by_vpid(pid) : current;	if (!tsk) {		rcu_read_unlock();		return -ESRCH;	}	ret = check_prlimit_permission(tsk, checkflags);	if (ret) {		rcu_read_unlock();		return ret;	}	get_task_struct(tsk);	rcu_read_unlock(); 	need_tasklist = !same_thread_group(tsk, current);	if (need_tasklist) {		/*		 * Ensure we can't race with group exit or de_thread(),		 * so tsk->group_leader can't be freed or changed until		 * read_unlock(tasklist_lock) below.		 */		read_lock(&tasklist_lock);		if (!pid_alive(tsk))			ret = -ESRCH;	} 	if (!ret) {		ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL,				old_rlim ? &old : NULL);	} 	if (need_tasklist)		read_unlock(&tasklist_lock); 	if (!ret && old_rlim) {		rlim_to_rlim64(&old, &old64);		if (copy_to_user(old_rlim, &old64, sizeof(old64)))			ret = -EFAULT;	} 	put_task_struct(tsk);	return ret;} SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim){	struct rlimit new_rlim; 	if (copy_from_user(&new_rlim, rlim, sizeof(*rlim)))		return -EFAULT;	return do_prlimit(current, resource, &new_rlim, NULL);} /* * It would make sense to put struct rusage in the task_struct, * except that would make the task_struct be *really big*.  After * task_struct gets moved into malloc'ed memory, it would * make sense to do this.  It will make moving the rest of the information * a lot simpler!  (Which we're not doing right now because we're not * measuring them yet). * * When sampling multiple threads for RUSAGE_SELF, under SMP we might have * races with threads incrementing their own counters.  But since word * reads are atomic, we either get new values or old values and we don't * care which for the sums.  We always take the siglock to protect reading * the c* fields from p->signal from races with exit.c updating those * fields when reaping, so a sample either gets all the additions of a * given child after it's reaped, or none so this sample is before reaping. * * Locking: * We need to take the siglock for CHILDEREN, SELF and BOTH * for  the cases current multithreaded, non-current single threaded * non-current multithreaded.  Thread traversal is now safe with * the siglock held. * Strictly speaking, we donot need to take the siglock if we are current and * single threaded,  as no one else can take our signal_struct away, no one * else can  reap the  children to update signal->c* counters, and no one else * can race with the signal-> fields. If we do not take any lock, the * signal-> fields could be read out of order while another thread was just * exiting. So we should  place a read memory barrier when we avoid the lock. * On the writer side,  write memory barrier is implied in  __exit_signal * as __exit_signal releases  the siglock spinlock after updating the signal-> * fields. But we don't do this yet to keep things simple. * */ static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r){	r->ru_nvcsw += t->nvcsw;	r->ru_nivcsw += t->nivcsw;	r->ru_minflt += t->min_flt;	r->ru_majflt += t->maj_flt;	r->ru_inblock += task_io_get_inblock(t);	r->ru_oublock += task_io_get_oublock(t);} void getrusage(struct task_struct *p, int who, struct rusage *r){	struct task_struct *t;	unsigned long flags;	u64 tgutime, tgstime, utime, stime;	unsigned long maxrss;	struct mm_struct *mm;	struct signal_struct *sig = p->signal;	unsigned int seq = 0; retry:	memset(r, 0, sizeof(*r));	utime = stime = 0;	maxrss = 0; 	if (who == RUSAGE_THREAD) {		task_cputime_adjusted(current, &utime, &stime);		accumulate_thread_rusage(p, r);		maxrss = sig->maxrss;		goto out_thread;	} 	flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq); 	switch (who) {	case RUSAGE_BOTH:	case RUSAGE_CHILDREN:		utime = sig->cutime;		stime = sig->cstime;		r->ru_nvcsw = sig->cnvcsw;		r->ru_nivcsw = sig->cnivcsw;		r->ru_minflt = sig->cmin_flt;		r->ru_majflt = sig->cmaj_flt;		r->ru_inblock = sig->cinblock;		r->ru_oublock = sig->coublock;		maxrss = sig->cmaxrss; 		if (who == RUSAGE_CHILDREN)			break;		fallthrough; 	case RUSAGE_SELF:		r->ru_nvcsw += sig->nvcsw;		r->ru_nivcsw += sig->nivcsw;		r->ru_minflt += sig->min_flt;		r->ru_majflt += sig->maj_flt;		r->ru_inblock += sig->inblock;		r->ru_oublock += sig->oublock;		if (maxrss < sig->maxrss)			maxrss = sig->maxrss; 		rcu_read_lock();		__for_each_thread(sig, t)			accumulate_thread_rusage(t, r);		rcu_read_unlock(); 		break; 	default:		BUG();	} 	if (need_seqretry(&sig->stats_lock, seq)) {		seq = 1;		goto retry;	}	done_seqretry_irqrestore(&sig->stats_lock, seq, flags); 	if (who == RUSAGE_CHILDREN)		goto out_children; 	thread_group_cputime_adjusted(p, &tgutime, &tgstime);	utime += tgutime;	stime += tgstime; out_thread:	mm = get_task_mm(p);	if (mm) {		setmax_mm_hiwater_rss(&maxrss, mm);		mmput(mm);	} out_children:	r->ru_maxrss = maxrss * (PAGE_SIZE / 1024); /* convert pages to KBs */	r->ru_utime = ns_to_kernel_old_timeval(utime);	r->ru_stime = ns_to_kernel_old_timeval(stime);} SYSCALL_DEFINE2(getrusage, int, who, struct rusage __user *, ru){	struct rusage r; 	if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN &&	    who != RUSAGE_THREAD)		return -EINVAL; 	getrusage(current, who, &r);	return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;} #ifdef CONFIG_COMPATCOMPAT_SYSCALL_DEFINE2(getrusage, int, who, struct compat_rusage __user *, ru){	struct rusage r; 	if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN &&	    who != RUSAGE_THREAD)		return -EINVAL; 	getrusage(current, who, &r);	return put_compat_rusage(&r, ru);}#endif SYSCALL_DEFINE1(umask, int, mask){	mask = xchg(&current->fs->umask, mask & S_IRWXUGO);	return mask;} static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd){	CLASS(fd, exe)(fd);	struct inode *inode;	int err; 	if (fd_empty(exe))		return -EBADF; 	inode = file_inode(fd_file(exe)); 	/*	 * Because the original mm->exe_file points to executable file, make	 * sure that this one is executable as well, to avoid breaking an	 * overall picture.	 */	if (!S_ISREG(inode->i_mode) || path_noexec(&fd_file(exe)->f_path))		return -EACCES; 	err = file_permission(fd_file(exe), MAY_EXEC);	if (err)		return err; 	return replace_mm_exe_file(mm, fd_file(exe));} /* * Check arithmetic relations of passed addresses. * * WARNING: we don't require any capability here so be very careful * in what is allowed for modification from userspace. */static int validate_prctl_map_addr(struct prctl_mm_map *prctl_map){	unsigned long mmap_max_addr = TASK_SIZE;

Lines 1501–2000 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