Oracle 8 ARM64 5.15内核无法修改sys_call_table读写权限
环境信息
- 内核版本:5.15.0-205.149.5.1.el8uek.aarch64(全5.15系列)
- 操作系统:Oracle 8 ARM64
问题描述
我尝试对sys_call_table做钩子(hook),需要将其权限改为读写,但执行以下代码时失败,返回值为-22:
// SPDX-License-Identifier: GPL-3.0 #include <linux/init.h> // module_{init,exit}() #include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ... #include <linux/kernel.h> // printk(), pr_*() #include <linux/kallsyms.h> // kallsyms_lookup_name() #include <asm/syscall.h> // syscall_fn_t, __NR_* #include <asm/ptrace.h> // struct pt_regs #include <asm/tlbflush.h> // flush_tlb_kernel_range() #include <asm/pgtable.h> // {clear,set}_pte_bit(), set_pte() #include <linux/vmalloc.h> // vm_unmap_aliases() #include <linux/mm.h> // struct mm_struct, apply_to_page_range() #include <linux/kconfig.h> // IS_ENABLED() #ifdef pr_fmt #undef pr_fmt #endif #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt static struct mm_struct *init_mm_ptr; static syscall_fn_t *syscall_table; static syscall_fn_t original_read; /********** HELPERS **********/ // From arch/arm64/mm/pageattr.c. struct page_change_data { pgprot_t set_mask; pgprot_t clear_mask; }; // From arch/arm64/mm/pageattr.c. static int change_page_range(pte_t *ptep, 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; } // From arch/arm64/mm/pageattr.c. 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_ptr, start, size, change_page_range, &data); flush_tlb_kernel_range(start, start + size); return ret; } // Simplified set_memory_rw() from arch/arm64/mm/pageattr.c. static int set_page_rw(unsigned long addr) { vm_unmap_aliases(); return __change_memory_common(addr, PAGE_SIZE, __pgprot(PTE_WRITE), __pgprot(PTE_RDONLY)); } // Simplified set_memory_ro() from arch/arm64/mm/pageattr.c. static int set_page_ro(unsigned long addr) { vm_unmap_aliases(); return __change_memory_common(addr, PAGE_SIZE, __pgprot(PTE_RDONLY), __pgprot(PTE_WRITE)); } /********** ACTUAL MODULE **********/ static long myread(const struct pt_regs *regs) { pr_info("read() called\n"); return original_read(regs); } static int __init modinit(void) { int res; pr_info("init\n"); // Shouldn't fail. init_mm_ptr = (struct mm_struct *)kallsyms_lookup_name("init_mm"); syscall_table = (syscall_fn_t *)kallsyms_lookup_name("sys_call_table"); original_read = syscall_table[__NR_read]; res = set_page_rw((unsigned long)(syscall_table + __NR_read) & PAGE_MASK); if (res != 0) { pr_err("set_page_rw() failed: %d\n", res); return res; } syscall_table[__NR_read] = myread; res = set_page_ro((unsigned long)(syscall_table + __NR_read) & PAGE_MASK); if (res != 0) { pr_err("set_page_ro() failed: %d\n", res); return res; } pr_info("init done\n"); return 0; } static void __exit modexit(void) { int res; pr_info("exit\n"); res = set_page_rw((unsigned long)(syscall_table + __NR_read) & PAGE_MASK); if (res != 0) { pr_err("set_page_rw() failed: %d\n", res); return; } syscall_table[__NR_read] = original_read; res = set_page_ro((unsigned long)(syscall_table + __NR_read) & PAGE_MASK); if (res != 0) pr_err("set_page_ro() failed: %d\n", res); pr_info("goodbye\n"); } module_init(modinit); module_exit(modexit); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL");
相同代码在5.4.17-2136.331.7.el8uek.aarch64及以下全5.4系列内核中可正常运行。
测试验证
我通过vmalloc创建内核内存页,使用相同的权限修改函数可成功将其设为只读或读写:
char *foo; static int __init modinit(void) { int res; pr_info("init\n"); // Shouldn't fail. init_mm_ptr = (struct mm_struct *)kallsyms_lookup_name("init_mm"); syscall_table = (syscall_fn_t *)kallsyms_lookup_name("sys_call_table"); foo = vmalloc(PAGE_SIZE); res = set_page_ro((unsigned long)foo); // 返回0(成功) foo[420] = '1'; // 此处崩溃 res = set_page_rw((unsigned long)foo); foo[420] = '1'; // 无崩溃,set_page_rw返回0(成功) pr_info("init done\n"); return 0; }
调试更新
我深入分析了apply_to_page_range、apply_to_p4d_range等页表操作函数,自定义相关函数调试后发现:
函数调用栈:
apply_to_pmd_rangepmd_leaf(在my_apply_to_pmd_range中触发WARN_ON_ONCE(my_pmd_leaf(*pmd)))!pmd_table(宏定义:#define pmd_leaf(pmd) (pmd_present(pmd) && !pmd_table(pmd)))
针对sys_call_table时!pmd_table返回1,针对vmalloc分配的页时返回0,这是唯一差异。
自定义函数:
int my_pmd_leaf(pmd_t pmd){ printk("inside %s\n", __FUNCTION__); int a1 = pmd_present(pmd), a2 = !pmd_table(pmd), a3 = a1 && a2; printk("a1=%d, a2=%d, a3=%d\n", a1, a2, a3); return a3; }
输出结果:
vmalloc page [71627.367740] inside my_pmd_leaf [71627.369595] a1=1, a2=0, a3=0 sys_call_table [71756.857568] inside my_pmd_leaf [71756.859347] a1=1, a2=1, a3=1
内容的提问来源于Stack Exchange,提问作者Kenzo
相关产品推荐
相关产品推荐

