Linux Kernel ARM64架构中, 如何突破内存只读的限制-remap方案

两个方案

  • 方案1 将原来只读的内存地址, 重新映射到一个可写的页面
  • 方案2 获取内存地址所在的pte, 修改pte属性为可写

今天我们来讨论方案1, 方案2在这篇文章中讨论

步骤如下

  1. 使用phys_to_page()获取指定地址所在的page
  2. 使用vmap()page重新映射为可写

使用phys_to_page()获取指定地址所在的page
由于要写入的数据可能跨越2个PAGE, 我们需要先计算需要映射的页面数

//
// 计算所需page个数
// offset_in_page()获取addr在页内的偏移量, 即占一个页内的多少字节, 加上len可能会超过1个page的大小
// DIV_ROUND_UP()向上取整, 计算总共需要几个page
//
int page_num = DIV_ROUND_UP(offset_in_page(addr) + len, PAGE_SIZE);

然后我们获取需要的page, 为方便阅读我删除了错误处理的代码

// 获取指定地址所在页面的起始地址
#define base_of_page(x) ((void*)((unsigned long)(x) & PAGE_MASK))

// 获取给定地址所在的页面
static int __get_addr_pages(void* addr, struct page *pages[], int page_num)
{
    int i;
    void* page_base_addr = base_of_page(addr);
    for (i = 0; i < page_num; i++) {
        pages[i] = phys_to_page(__pa(page_base_addr));
        if (!pages[i]){
            return -EFAULT;
        }
        // 下一个page的起始地址
        page_base_addr += PAGE_SIZE;
    }
    return 0;
}

使用vmap()将page重新映射为可写
代码如下, 为方便阅读我删除了错误处理的代码

// 重新映射可写
static void* remap_with_write_permissions(void* addr, int len)
{
    void* writeable_addr;
    int page_num = DIV_ROUND_UP(offset_in_page(addr) + len, PAGE_SIZE);
    struct page **pages = kmalloc(page_num * sizeof(*pages), GFP_KERNEL);
    if (!pages){
        return NULL;
    }
    
    // 获取addr对应的page
    if (__get_addr_`pages(addr, pages, page_num)){
        goto err;
    }
    // 重新映射page到可写的地址
    writeable_addr = vmap(pages, page_num, VM_MAP, PAGE_KERNEL);
    kfree(pages);

    return writeable_addr + offset_in_page(addr);
err:
    kfree(pages);
    return NULL;
}

映射成功之后会返回一个可以写入的地址, 使用此地址就可以写入数据了, 示例如下

int __my_memcpy(void *dst, void *src, int len)
{
    void *new_dst = remap_with_write_permissions(dst, len);
    if(new_dst){
        memcpy(new_dst, src, len);
        vunmap(base_of_page(new_dst));
    }
    return 0;
}

参考资料
https://gist.github.com/ohnx/02c181c2674ca6db88ddd12b36e6c1bf#file-ttgl-c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值