用户态mprotect的定义
int mprotect(const void *addr, size_t len, int prot);
其中:addr --- 起始地址
len ----addr开始的一块内存的大小
prot ---- 期望设置的内存属性,取值是PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE 这些bit的组合
SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
unsigned long, prot)
{
unsigned long vm_flags, nstart, end, tmp, reqprot;
struct vm_area_struct *vma, *prev;
int error = -EINVAL;
/* 取出prot的GROWSDOWN GROWSUP 的属性后, 将prot里面的这两个bit掩掉 */
const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP);
if (grows == (PROT_GROWSDOWN|PROT_GROWSUP)) /* can't be both */ /* GROWSDOWN GROWSUP两个属性是互斥的,不能同时存在 */
return -EINVAL;
if (start & ~PAGE_MASK) /* 起始地址需要按页对齐 */
return -EINVAL;
if (!len) /* 长度不能为0 */
return 0;
len = PAGE_ALIGN(len); /* 长度需要按页对齐 */
end = start + len;
if (end <= start)
return -ENOMEM;
#ifdef CONFIG_PAX_SEGMEXEC
if (current->mm->pax_flags & MF_PAX_SEGMEXEC) {
if (end > SEGMEXEC_TASK_SIZE)
return -EINVAL;
} else
#endif
if (end > TASK_SIZE)
return -EINVAL;
if (!arch_validate_prot(prot)) /* 检查prot */
return -EINVAL;
reqprot = prot;
/*
* Does the application expect PROT_READ to imply PROT_EXEC:
*/
/* 如果程序有READ_IMPLIES_EXEC标志,则修改prot具备EXEC标志 */
if ((prot & (PROT_READ | PROT_WRITE)) && (current->personality & READ_IMPLIES_EXEC))
prot |= PROT_EXEC;
/* 将PROT_READ, PROT_WRITE, PROT_EXEC 这些标志位转换为VM_READ, VM_WRITE, VM_EXEC */
vm_flags = calc_vm_prot_bits(prot);
down_write(¤t->mm->mmap_sem);
vma = find_vma_prev(current->mm, start, &prev);
error = -ENOMEM;
if (!vma)
goto out;
if (unlikely(grows & PROT_GROWSDOWN)) {
if (vma->vm_start >= end)
goto out;
start = vma->vm_start;
error = -EINVAL;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto out;
}
else {
if (vma->vm_start > start)
goto out;
if (unlikely(grows & PROT_GROWSUP)) {
end = vma->vm_end;
error = -EINVAL;
if (!(vma->vm_flags & VM_GROWSUP))
goto out;
}
}
if (start > vma->vm_start)
prev = vma;
#ifdef CONFIG_PAX_MPROTECT
if (current->mm->binfmt && current->mm->binfmt->handle_mprotect)
current->mm->binfmt->handle_mprotect(vma, vm_flags);
#endif
/* 到此vma已经找到 */
for (nstart = start ; ; ) {
unsigned long newflags;
/* Here we know that vma->vm_start <= nstart < vma->vm_end. */
/* vm_flags 是prot装换而来,是应用要求达到的页属性,包括读,写,执行属性
vma->vm_flags 是页面当前具备的属性
(vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC) 是要把页面当前属性的读,写,执行三bit掩掉
newflags = vm_flags | (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC))
是把应用要求的读写执行属性结合vma->vm_flags 记录到newflags
*/
newflags = vm_flags | (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC));
/* newflags >> 4 shift VM_MAY% in place of VM_% */
/*
#define VM_READ 0x00000001 /* currently active flags */
#define VM_WRITE 0x00000002
#define VM_EXEC 0x00000004
#define VM_SHARED 0x00000008
/* mprotect() hardcodes VM_MAYREAD >> 4 == VM_READ, and so for r/w/x bits. */
#define VM_MAYREAD 0x00000010 /* limits for mprotect() etc */
#define VM_MAYWRITE 0x00000020
#define VM_MAYEXEC 0x00000040
#define VM_MAYSHARE 0x00000080
VM_READ VM_WRITE VM_EXEC 是 0, 1, 2 bit
VM_MAYREAD VM_MAYWRITE VM_MAYEXEC 是 4, 5, 6bit
将newflags 右移四位, 实际上0-3bit上显示的是VM_MAY% 的属性;
比如newflags的RWX属性是 1 1 1 这个属性是目标属性
而May_% 属性是 1 1 0 这个属性是是否允许设置的属性
May_% 属性取反 0 0 1
--------------------------------------------------------------------------------------------
RWX属性&May_%属性取反 0 0 1 bit 为1的属性表示矛盾的属性,即希望使能某属性而该属性不允许被使能
*/
if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) {
if (prot & (PROT_WRITE | PROT_EXEC)) /* 如果write和exec属性要求设置,而不允许设置,这里要log */
gr_log_rwxmprotect(vma);
error = -EACCES;
goto out;
}
/* 对于heap和stack, 目前的做法是允许设置EXEC权限,但是要在log里面记录一下 */
#ifdef CONFIG_PAX_MPROTECT_WARNONLY
if (current->mm->pax_flags & MF_PAX_MPROTECT) {
if (prot & PROT_EXEC) {
/* warning only on heap */
if (vma->vm_start <= current->mm->brk &&
vma->vm_end >= current->mm->start_brk)
gr_log_rwxmprotect(vma);
/* warning only on stack */
else if (vma->vm_flags & (VM_GROWSDOWN | VM_GROWSUP))
gr_log_rwxmprotect(vma);
}
}
#endif
if (!gr_acl_handle_mprotect(vma->vm_file, prot)) {
error = -EACCES;
goto out;
}
error = security_file_mprotect(vma, reqprot, prot);
if (error)
goto out;
tmp = vma->vm_end;
if (tmp > end)
tmp = end;
error = mprotect_fixup(vma, &prev, nstart, tmp, newflags);
if (error)
goto out;
track_exec_limit(current->mm, nstart, tmp, vm_flags);
nstart = tmp;
if (nstart < prev->vm_end)
nstart = prev->vm_end;
if (nstart >= end)
goto out;
vma = prev->vm_next;
if (!vma || vma->vm_start != nstart) {
error = -ENOMEM;
goto out;
}
}
out:
up_write(¤t->mm->mmap_sem);
return error;
阅读(4661) | 评论(0) | 转发(0) |