diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-01-13 15:00:22 +0000 | 
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-01-13 15:00:22 +0000 | 
| commit | 4de3a8e101150feaefa1139611a50ff37467f33e (patch) | |
| tree | daada742542518b02d7db7c5d32e715eaa5f166d /kernel/sys.c | |
| parent | 294064f58953f9964e5945424b09c51800330a83 (diff) | |
| parent | 099469502f62fbe0d7e4f0b83a2f22538367f734 (diff) | |
Merge branch 'master' into fixes
Diffstat (limited to 'kernel/sys.c')
| -rw-r--r-- | kernel/sys.c | 121 | 
1 files changed, 121 insertions, 0 deletions
| diff --git a/kernel/sys.c b/kernel/sys.c index ddf8155bf3f..40701538fbd 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -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; | 
