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

两个方案

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

今天我们来讨论方案2

事实上, Linux内核已经提供了相关的函数set_memory_rw, 但是此函数有个限制, 那就是只能修改通过vmalloc或者vmap申请的内存地址, 我们可以参考它实现一个无此限制的函数

实际上直接把vm_area的检测删除即可, 代码如下

static int change_memory_common(unsigned long addr, int numpages,
                pgprot_t set_mask, pgprot_t clear_mask)
{
    unsigned long start = addr;
    unsigned long size = PAGE_SIZE*numpages;
    unsigned long end = start + size;
    struct vm_struct *area;

    if (!PAGE_ALIGNED(addr)) {
        start &= PAGE_MASK;
        end = start + size;
        WARN_ON_ONCE(1);
    }

    if (!numpages)
        return 0;

    return __change_memory_common(start, size, set_mask, clear_mask);
}

其中最重要的是__change_memory_common函数, 我们看下它的实现


/*
 * This function assumes that the range is mapped with PAGE_SIZE pages.
 */
static int __change_memory_common(unsigned long start, unsigned long size,
                pgprot_t set_mask, pgprot_t clear_mask)
{
    struct page_change_data data;
    int ret;

    data.set_mask = set_mask;
    data.clear_mask = clear_mask;

    ret = apply_to_page_range(&init_mm, start, size, change_page_range,
                    &data);

    flush_tlb_kernel_range(start, start + size);
    return ret;
}

最重要的就是这个函数apply_to_page_range, 此函数会遍历要修改的内存范围所在的pte, 针对范围内的每一个pte调用我们提供的回调函数change_page_range, 回调函数实现如下

static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
            void *data)
{
    struct page_change_data *cdata = data;
    pte_t pte = READ_ONCE(*ptep);

    pte = clear_pte_bit(pte, cdata->clear_mask);
    pte = set_pte_bit(pte, cdata->set_mask);

    set_pte(ptep, pte);
    return 0;
}

我们直接在回调函数中修改pte的属性即可, 最后__change_memory_common会调用flush_tlb_kernel_range刷新tlb完成修改

使用方法如下, 设置指定的地址可以读写:

int set_memory_rw(unsigned long addr, int numpages)
{
    return change_memory_common(addr, numpages,
                    __pgprot(PTE_WRITE),
                    __pgprot(PTE_RDONLY));
}

参考资料
https://elixir.bootlin.com/linux/v4.19.273/source/arch/arm64/mm/pageattr.c
https://elixir.bootlin.com/linux/v4.19.273/source/mm/memory.c

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容