How to write to protected pages in the Linux kernel?

纵然是瞬间 提交于 2020-02-24 13:48:05

问题


I am trying to add a syscall in a module. My rationale is:

  1. This is for a research project, so the exact implementation does not matter.
  2. Adding syscalls in the kernel-core takes a prohibitively long time to re-compile. I can suck up compiling once with an expanded syscall table, but not every time. Even with incremental compiling, linking and archiving the final binary takes a long time.
  3. Since the project is timing sensitive, using kprobes to intercept the syscall handler would slow down the syscall handler.

I am still open to other means of adding a syscall, but for the above reasons, I think that writing to the sys_call_table in a module is the cleanest way to do what I am trying to do.

I've gotten the address of the syscall table from the System.map, disabled kaslr, and I am trying to clear the page protections, but some write-protection is still holding me back.

// following https://web.iiit.ac.in/~arjun.nath/random_notes/modifying_sys_call.html

// clear cr0 write protection
write_cr0 (read_cr0 () & (~ 0x10000));

// clear page write protection
sys_call_table_page = virt_to_page(&sys_call_table[__NR_execves]);
set_pages_rw(sys_call_table_page, 1);

// do write
sys_call_table[__NR_execves] = sys_execves;

However, I'm still getting a permission error, but I don't know the mechanism by which it is enforced:

[   11.145647] ------------[ cut here ]------------
[   11.148893] CR0 WP bit went missing!?
[   11.151539] WARNING: CPU: 0 PID: 749 at arch/x86/kernel/cpu/common.c:386 native_write_cr0+0x3e/0x70
...
Here was a call trace pointing to the write of sys_call_table
...
[   11.332825] ---[ end trace c20c95651874c08b ]---
[   11.336056] CPA  protect  Rodata RO: 0xffff888002804000 - 0xffff888002804fff PFN 2804 req 8000000000000063 prevent 0000000000000002
[   11.343934] CPA  protect  Rodata RO: 0xffffffff82804000 - 0xffffffff82804fff PFN 2804 req 8000000000000163 prevent 0000000000000002
[   11.351720] BUG: unable to handle page fault for address: ffffffff828040e0
[   11.356418] #PF: supervisor write access in kernel mode
[   11.359908] #PF: error_code(0x0003) - permissions violation
[   11.363665] PGD 3010067 P4D 3010067 PUD 3011063 PMD 31e29063 PTE 8000000002804161
[   11.368701] Oops: 0003 [#1] SMP KASAN PTI

full dmesg

Any guesses on how to disable it?


回答1:


The kernel has code to protect against this sort of action.

First, the kernel by default does not allow you to remove write protection from the cr0 register. It checks that in arch/x86/kernel/cpu/common.c:native_write_cr0

if (static_branch_likely(&cr_pinning)) {
    if (unlikely((val & X86_CR0_WP) != X86_CR0_WP)) {
        bits_missing = X86_CR0_WP;
        val |= bits_missing;
        goto set_register;
    }
    /* Warn after we've set the missing bits. */
    WARN_ONCE(bits_missing, "CR0 WP bit went missing!?\n");
}

Second, the page table does not allow you to set a page that should be read-only to read-write. It does that check arch/x86/mm/pageattr.c:static_protections

/* Check the PFN directly */
res = protect_rodata(pfn, pfn + npg - 1);
check_conflict(warnlvl, prot, res, start, end, pfn, "Rodata RO");
forbidden |= res;

If you disable these two checks by removing both blobs, the code to change the pagetable works.




回答2:


It is possible to just remap the sys_call_table as read-write using the set_memory_rw function, so it's possible to write to it without disabling write protection for the whole kernel. Used this method myself on aarch64, not sure if it works on x86.



来源:https://stackoverflow.com/questions/58512430/how-to-write-to-protected-pages-in-the-linux-kernel

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!