簡體   English   中英

如何查看地址在kernel空間是否可以訪問

[英]How to check an address is accessible in the kernel space

我正在研究 kernel 實時補丁,實時補丁模塊中的一些代碼如下所示:

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.
    }
}

此代碼獲取 kernel 符號的地址(類型為struct list_head )並嘗試迭代列表。 但由於某些原因,鏈表中的某些節點可能會被破壞,導致某些節點的next指針無效(如 NULL、0xABABABAB 或其他隨機數),並且取消引用next指針可能會導致 kernel 崩潰。

那么有沒有辦法檢查一個指針是否可以安全訪問呢?

我檢查了兩個以前的答案:

如何檢查 memory 地址在 Linux kernel 中是否有效?

如何判斷虛擬地址是否在 ARM linux kernel 中具有有效映射?

他們告訴我使用virt_addr_valid 我有一些肯定可以訪問的地址,比如 0xFFFFFFFFA032C040,但是virt_addr_valid總是返回 false,這讓我無法區分實時補丁模塊中的“可訪問”和“不可訪問”地址。

如此所述, kallsyms_lookup_name()提出了一些許可問題,並且在當前內核上未導出。

您可能需要檢查livepatch

在我的例子中,我想檢查的 memory 地址應該使用kmalloc()分配,但由於一些錯誤可能會被污染(即隨機值)。

virt_addr_valid()檢查地址是否位於 kernel 中的“內核代碼區域”或“直接映射區域”中(查看此鏈接以了解 x86_64 memory 布局)。 並且由kmalloc()分配的 memory 位於“直接映射區域”中,因此在 kmalloc 的內存上使用virt_addr_valid()總是正確的。 但是另一方面,由於我的實驗,一些地址可能會得到virt_addr_valid=true,但是無法訪問,取消引用地址可能會導致機器崩潰。 所以我還需要確保地址在頁表中正確映射,以免機器崩潰。

所以解決方案包含兩個步驟:

  1. virt_addr_valid()是否在地址上返回 true
  2. 如果為 true,則執行 page-table-walk 以檢查地址是否正確映射

由於“virt_addr_valid-is-true”區域中的memory映射沒有改變,所以不需要持有鎖。

下面是帶有 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;
}

我花了一段時間,但我發現 source/arch/x86/mm/init_64.c 中的 kern_addr_valid(addr) 可以解決問題。 它遍歷頁表,計算大頁以確保地址有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM