diff options
Diffstat (limited to 'kernel/sys.c')
| -rw-r--r-- | kernel/sys.c | 127 | 
1 files changed, 124 insertions, 3 deletions
| diff --git a/kernel/sys.c b/kernel/sys.c index 481611fbd079..40701538fbd1 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1605,7 +1605,7 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)  	unsigned long maxrss = 0;  	memset((char *) r, 0, sizeof *r); -	utime = stime = cputime_zero; +	utime = stime = 0;  	if (who == RUSAGE_THREAD) {  		task_times(current, &utime, &stime); @@ -1635,8 +1635,8 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)  		case RUSAGE_SELF:  			thread_group_times(p, &tgutime, &tgstime); -			utime = cputime_add(utime, tgutime); -			stime = cputime_add(stime, tgstime); +			utime += tgutime; +			stime += tgstime;  			r->ru_nvcsw += p->signal->nvcsw;  			r->ru_nivcsw += p->signal->nivcsw;  			r->ru_minflt += p->signal->min_flt; @@ -1692,6 +1692,124 @@ SYSCALL_DEFINE1(umask, int, mask)  	return mask;  } +#ifdef CONFIG_CHECKPOINT_RESTORE +static int prctl_set_mm(int opt, unsigned long addr, +			unsigned long arg4, unsigned long arg5) +{ +	unsigned long rlim = rlimit(RLIMIT_DATA); +	unsigned long vm_req_flags; +	unsigned long vm_bad_flags; +	struct vm_area_struct *vma; +	int error = 0; +	struct mm_struct *mm = current->mm; + +	if (arg4 | arg5) +		return -EINVAL; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	if (addr >= TASK_SIZE) +		return -EINVAL; + +	down_read(&mm->mmap_sem); +	vma = find_vma(mm, addr); + +	if (opt != PR_SET_MM_START_BRK && opt != PR_SET_MM_BRK) { +		/* It must be existing VMA */ +		if (!vma || vma->vm_start > addr) +			goto out; +	} + +	error = -EINVAL; +	switch (opt) { +	case PR_SET_MM_START_CODE: +	case PR_SET_MM_END_CODE: +		vm_req_flags = VM_READ | VM_EXEC; +		vm_bad_flags = VM_WRITE | VM_MAYSHARE; + +		if ((vma->vm_flags & vm_req_flags) != vm_req_flags || +		    (vma->vm_flags & vm_bad_flags)) +			goto out; + +		if (opt == PR_SET_MM_START_CODE) +			mm->start_code = addr; +		else +			mm->end_code = addr; +		break; + +	case PR_SET_MM_START_DATA: +	case PR_SET_MM_END_DATA: +		vm_req_flags = VM_READ | VM_WRITE; +		vm_bad_flags = VM_EXEC | VM_MAYSHARE; + +		if ((vma->vm_flags & vm_req_flags) != vm_req_flags || +		    (vma->vm_flags & vm_bad_flags)) +			goto out; + +		if (opt == PR_SET_MM_START_DATA) +			mm->start_data = addr; +		else +			mm->end_data = addr; +		break; + +	case PR_SET_MM_START_STACK: + +#ifdef CONFIG_STACK_GROWSUP +		vm_req_flags = VM_READ | VM_WRITE | VM_GROWSUP; +#else +		vm_req_flags = VM_READ | VM_WRITE | VM_GROWSDOWN; +#endif +		if ((vma->vm_flags & vm_req_flags) != vm_req_flags) +			goto out; + +		mm->start_stack = addr; +		break; + +	case PR_SET_MM_START_BRK: +		if (addr <= mm->end_data) +			goto out; + +		if (rlim < RLIM_INFINITY && +		    (mm->brk - addr) + +		    (mm->end_data - mm->start_data) > rlim) +			goto out; + +		mm->start_brk = addr; +		break; + +	case PR_SET_MM_BRK: +		if (addr <= mm->end_data) +			goto out; + +		if (rlim < RLIM_INFINITY && +		    (addr - mm->start_brk) + +		    (mm->end_data - mm->start_data) > rlim) +			goto out; + +		mm->brk = addr; +		break; + +	default: +		error = -EINVAL; +		goto out; +	} + +	error = 0; + +out: +	up_read(&mm->mmap_sem); + +	return error; +} +#else /* CONFIG_CHECKPOINT_RESTORE */ +static int prctl_set_mm(int opt, unsigned long addr, +			unsigned long arg4, unsigned long arg5) +{ +	return -EINVAL; +} +#endif +  SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,  		unsigned long, arg4, unsigned long, arg5)  { @@ -1841,6 +1959,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,  			else  				error = PR_MCE_KILL_DEFAULT;  			break; +		case PR_SET_MM: +			error = prctl_set_mm(arg2, arg3, arg4, arg5); +			break;  		default:  			error = -EINVAL;  			break; | 
