|
|
return prev ? prev->vm_next : vma; | return prev ? prev->vm_next : vma; |
} | } |
| |
|
/* |
|
* Verify that the stack growth is acceptable and |
|
* update accounting. This is shared with both the |
|
* grow-up and grow-down cases. |
|
*/ |
|
static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, unsigned long grow) |
|
{ |
|
struct mm_struct *mm = vma->vm_mm; |
|
struct rlimit *rlim = current->signal->rlim; |
|
|
|
/* address space limit tests */ |
|
rlim = current->signal->rlim; |
|
if (mm->total_vm + grow > rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT) |
|
return -ENOMEM; |
|
|
|
/* Stack limit test */ |
|
if (size > rlim[RLIMIT_STACK].rlim_cur) |
|
return -ENOMEM; |
|
|
|
/* mlock limit tests */ |
|
if (vma->vm_flags & VM_LOCKED) { |
|
unsigned long locked; |
|
unsigned long limit; |
|
locked = mm->locked_vm + grow; |
|
limit = rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT; |
|
if (locked > limit && !capable(CAP_IPC_LOCK)) |
|
return -ENOMEM; |
|
} |
|
|
|
/* |
|
* Overcommit.. This must be the final test, as it will |
|
* update security statistics. |
|
*/ |
|
if (security_vm_enough_memory(grow)) |
|
return -ENOMEM; |
|
|
|
/* Ok, everything looks good - let it rip */ |
|
mm->total_vm += grow; |
|
if (vma->vm_flags & VM_LOCKED) |
|
mm->locked_vm += grow; |
|
__vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow); |
|
return 0; |
|
} |
|
|
#ifdef CONFIG_STACK_GROWSUP | #ifdef CONFIG_STACK_GROWSUP |
/* | /* |
* vma is the first one with address > vma->vm_end. Have to extend vma. | * vma is the first one with address > vma->vm_end. Have to extend vma. |
*/ | */ |
int expand_stack(struct vm_area_struct * vma, unsigned long address) | int expand_stack(struct vm_area_struct * vma, unsigned long address) |
{ | { |
unsigned long grow; |
int error; |
| |
if (!(vma->vm_flags & VM_GROWSUP)) | if (!(vma->vm_flags & VM_GROWSUP)) |
return -EFAULT; | return -EFAULT; |
|
Lines 1345-1372
int expand_stack(struct vm_area_struct *
|
Link Here
|
|---|
|
*/ | */ |
address += 4 + PAGE_SIZE - 1; | address += 4 + PAGE_SIZE - 1; |
address &= PAGE_MASK; | address &= PAGE_MASK; |
grow = (address - vma->vm_end) >> PAGE_SHIFT; |
error = 0; |
| |
/* Overcommit.. */ |
/* Somebody else might have raced and expanded it already */ |
if (security_vm_enough_memory(grow)) { |
if (address > vma->vm_end) { |
anon_vma_unlock(vma); |
unsigned long size, grow; |
return -ENOMEM; |
|
} |
size = address - vma->vm_start; |
|
grow = (address - vma->vm_end) >> PAGE_SHIFT; |
if (address - vma->vm_start > current->signal->rlim[RLIMIT_STACK].rlim_cur || |
|
((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > |
error = acct_stack_growth(vma, size, grow); |
current->signal->rlim[RLIMIT_AS].rlim_cur) { |
if (!error) |
anon_vma_unlock(vma); |
vma->vm_end = address; |
vm_unacct_memory(grow); |
|
return -ENOMEM; |
|
} | } |
vma->vm_end = address; |
|
vma->vm_mm->total_vm += grow; |
error = acct_stack_growth(vma, size, grow); |
if (vma->vm_flags & VM_LOCKED) |
if (!error) |
vma->vm_mm->locked_vm += grow; |
vma->vm_end = address; |
__vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, grow); |
anon_vma_unlock(vma); |
anon_vma_unlock(vma); |
return error; |
return 0; |
|
} | } |
| |
struct vm_area_struct * | struct vm_area_struct * |
|
Lines 1391-1397
find_extend_vma(struct mm_struct *mm, un
|
Link Here
|
|---|
|
*/ | */ |
int expand_stack(struct vm_area_struct *vma, unsigned long address) | int expand_stack(struct vm_area_struct *vma, unsigned long address) |
{ | { |
unsigned long grow; |
int error; |
| |
/* | /* |
* We must make sure the anon_vma is allocated | * We must make sure the anon_vma is allocated |
|
Lines 1407-1435
int expand_stack(struct vm_area_struct *
|
Link Here
|
|---|
|
* anon_vma lock to serialize against concurrent expand_stacks. | * anon_vma lock to serialize against concurrent expand_stacks. |
*/ | */ |
address &= PAGE_MASK; | address &= PAGE_MASK; |
grow = (vma->vm_start - address) >> PAGE_SHIFT; |
error = 0; |
| |
/* Overcommit.. */ |
/* Somebody else might have raced and expanded it already */ |
if (security_vm_enough_memory(grow)) { |
if (address < vma->vm_start) { |
anon_vma_unlock(vma); |
unsigned long size, grow; |
return -ENOMEM; |
|
} |
size = vma->vm_end - address; |
|
grow = (vma->vm_start - address) >> PAGE_SHIFT; |
if (vma->vm_end - address > current->signal->rlim[RLIMIT_STACK].rlim_cur || |
|
((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > |
error = acct_stack_growth(vma, size, grow); |
current->signal->rlim[RLIMIT_AS].rlim_cur) { |
if (!error) { |
anon_vma_unlock(vma); |
vma->vm_start = address; |
vm_unacct_memory(grow); |
vma->vm_pgoff -= grow; |
return -ENOMEM; |
} |
} | } |
vma->vm_start = address; |
|
vma->vm_pgoff -= grow; |
|
vma->vm_mm->total_vm += grow; |
|
if (vma->vm_flags & VM_LOCKED) |
|
vma->vm_mm->locked_vm += grow; |
|
__vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, grow); |
|
anon_vma_unlock(vma); | anon_vma_unlock(vma); |
return 0; |
return error; |
} | } |
| |
struct vm_area_struct * | struct vm_area_struct * |