Chinaunix首页 | 论坛 | 博客
  • 博客访问: 435728
  • 博文数量: 99
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 1012
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-20 16:30
个人简介

linux kernel 工程师

文章分类

全部博文(99)

文章存档

2018年(5)

2017年(12)

2016年(27)

2015年(10)

2014年(43)

2012年(2)

我的朋友

分类: LINUX

2016-03-24 16:39:59

用户态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) |
给主人留下些什么吧!~~