微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何检查内核空间中的地址是否可访问

如何解决如何检查内核空间中的地址是否可访问

我正在开发内核实时补丁,实时补丁模块中的一些代码如下所示:

void list_checker() {
    struct list_head *head,*iter;
    head = (struct list_head *)kallsyms_lookup_name("module_name:symbol_name");
    for (iter = head->next; iter != head; iter = iter->next) {
        // do something.
    }
}

代码获取内核符号(类型为 struct list_head)的地址并尝试迭代列表。但由于某些原因,链表中的某些节点可能会被破坏,导致某些节点的next指针无效(如NULL、0xABABABAB或其他随机数),而取消引用next指针可能导致内核崩溃。

那么有没有办法检查一个指针是否可以安全访问?

我已经检查了两个以前的答案:

How to check a memory address is valid inside Linux kernel?

How can I tell if a virtual address has valid mapping in ARM linux kernel?

他们告诉我使用 virt_addr_valid我有一些肯定可以访问的地址,例如 0xFFFFFFFFA032C040,但是 virt_addr_valid 总是返回 false,这使我无法在实时补丁模块中区分“可访问”和“不可访问”地址。

解决方法

here 所述,kallsyms_lookup_name() 引起了一些许可问题,并且在当前内核中未导出。

您可能需要检查 livepatch

,

在我的情况下,我想检查的内存地址应该使用 kmalloc() 分配,但可能由于某些错误而被污染(即随机值)。

virt_addr_valid() 检查地址是否驻留在内核中的“内核代码区”或“直接映射区”(检查此 link 以获取 x86_64 内存布局)。并且由 kmalloc() 分配的内存驻留在“直接映射区域”中,因此在 kmalloc 的内存上使用 virt_addr_valid() 总是正确的。但另一方面,由于我的实验,某些地址可能会得到 virt_addr_valid=true ,但无法访问,并且取消引用该地址可能会导致机器崩溃。所以我还需要确保地址在页表中正确映射,以免机器崩溃。

所以解决方案包含两个步骤:

  1. virt_addr_valid() 是否在地址上返回 true
  2. 如果为 true,则执行 page-table-walk 以检查地址是否正确映射

由于“virt_addr_valid-is-true”区域的内存映射没有变化,所以不需要持有锁。

以下是带有 4 级页表的 x86_64 代码。

static bool page_mapping_exist(unsigned long addr,size_t size) {
    pgd_t *pgd;
    pmd_t *pmd;
    pud_t *pud;
    pte_t *pte;
    struct mm_struct *mm = current->mm;
    unsigned long end_addr;
    pgd = pgd_offset(mm,addr);
    if (unlikely(!pgd) || unlikely(pgd_none(*pgd)) || unlikely(!pgd_present(*pgd)) )
        return false;
    
    pud = pud_offset(pgd,addr);
    if (unlikely(!pud) || unlikely(pud_none(*pud)) || unlikely(!pud_present(*pud)))
        return false;

    pmd = pmd_offset(pud,addr);
    if (unlikely(!pmd) || unlikely(pmd_none(*pmd)) || unlikely(!pmd_present(*pmd)))
        return false;

    if (pmd_trans_huge(*pmd)) {
        end_addr = (((addr >> PMD_SHIFT) + 1) << PMD_SHIFT) - 1;
        goto end;
    }
    pte = pte_offset_map(pmd,addr);
    if (unlikely(!pte) || unlikely(!pte_present(*pte)))
        return false;
    end_addr = (((addr >> PAGE_SHIFT) + 1) << PAGE_SHIFT) - 1;
end:
    if (end_addr >= addr + size - 1)
        return true;
    return page_mapping_exist(end_addr + 1,size - (end_addr - addr + 1));
}

static bool addr_valid(unsigned long addr,size_t size) {
    int i;
    for (i = 0; i < size; i++) {
        if (!virt_addr_valid(addr + i))
            return false;
    }
    if (!page_mapping_exist(addr,size))
        return false;
    return true;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。