mirror of
https://github.com/choff/anbox-modules.git
synced 2026-10-17 16:55:48 +08:00
binder: call looked-up kernel functions through a push+ret trampoline with IBT
The kernel seals the ENDBR64 of functions it never calls indirectly (kallsyms_lookup_name, can_nice, the security_binder_* hooks...), so stepping back over ENDBR64 is not enough: there is none left, and the indirect call still raises #CP when binderfs is mounted. Reach these functions with push+ret, which IBT does not track.
This commit is contained in:
parent
63e80d8ea7
commit
a59d8a3b10
@ -9,11 +9,43 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/task_work.h>
|
||||
|
||||
#include "deps.h"
|
||||
|
||||
#ifdef CONFIG_X86_KERNEL_IBT
|
||||
/*
|
||||
* With IBT the kernel seals the ENDBR64 of functions it never calls
|
||||
* indirectly (kallsyms_lookup_name, can_nice, the security_binder_* hooks...),
|
||||
* so an indirect call to an address looked up here raises #CP (kernel BUG at
|
||||
* arch/x86/kernel/cet.c). Reach them with push+ret instead, which IBT does
|
||||
* not track; the callee then returns straight to our caller.
|
||||
*/
|
||||
unsigned long binder_ibt_call(void *fn, unsigned long a, unsigned long b,
|
||||
unsigned long c, unsigned long d);
|
||||
asm(".pushsection .text\n"
|
||||
".type binder_ibt_call, @function\n"
|
||||
"binder_ibt_call:\n"
|
||||
" mov %rdi, %r11\n"
|
||||
" mov %rsi, %rdi\n"
|
||||
" mov %rdx, %rsi\n"
|
||||
" mov %rcx, %rdx\n"
|
||||
" mov %r8, %rcx\n"
|
||||
" push %r11\n"
|
||||
" " ASM_RET
|
||||
".size binder_ibt_call, .-binder_ibt_call\n"
|
||||
".popsection\n");
|
||||
STACK_FRAME_NON_STANDARD(binder_ibt_call);
|
||||
#define IBT_ARGS(a, b, c, d, ...) \
|
||||
(unsigned long)(a), (unsigned long)(b), (unsigned long)(c), (unsigned long)(d)
|
||||
#define deps_call(fn, ...) binder_ibt_call((void *)(fn), IBT_ARGS(__VA_ARGS__, 0, 0, 0, 0))
|
||||
#else
|
||||
#define deps_call(fn, ...) (fn)(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0))
|
||||
|
||||
#ifndef CONFIG_KPROBES
|
||||
@ -42,20 +74,6 @@ static kallsyms_lookup_name_t get_kallsyms_lookup_name_ptr(void)
|
||||
addr = (kallsyms_lookup_name_t) probe.addr;
|
||||
unregister_kprobe(&probe);
|
||||
|
||||
#ifdef CONFIG_X86_KERNEL_IBT
|
||||
/*
|
||||
* With IBT, kprobes skip the function's ENDBR64, so probe.addr is 4
|
||||
* bytes past the entry. An indirect call there raises #CP; step back.
|
||||
*/
|
||||
{
|
||||
const u8 endbr64[] = { 0xf3, 0x0f, 0x1e, 0xfa };
|
||||
u8 *p = (u8 *)addr - sizeof(endbr64);
|
||||
|
||||
if (!memcmp(p, endbr64, sizeof(endbr64)))
|
||||
addr = (kallsyms_lookup_name_t)p;
|
||||
}
|
||||
#endif
|
||||
|
||||
return addr;
|
||||
}
|
||||
#endif
|
||||
@ -78,7 +96,7 @@ static unsigned long kallsyms_lookup_name_wrapper(const char *name)
|
||||
if (!func_ptr)
|
||||
func_ptr = get_kallsyms_lookup_name_ptr();
|
||||
|
||||
return func_ptr(name);
|
||||
return deps_call(func_ptr, name);
|
||||
#else
|
||||
return kallsyms_lookup_name(name);
|
||||
#endif
|
||||
@ -111,9 +129,9 @@ int __close_fd_get_file(unsigned int fd, struct file **res)
|
||||
#endif
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0))
|
||||
return close_fd_get_file_ptr(fd);
|
||||
return (struct file *)deps_call(close_fd_get_file_ptr, fd);
|
||||
#else
|
||||
return close_fd_get_file_ptr(fd, res);
|
||||
return (int)deps_call(close_fd_get_file_ptr, fd, res);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -123,7 +141,7 @@ int can_nice(const struct task_struct *p, const int nice)
|
||||
{
|
||||
if (!can_nice_ptr)
|
||||
can_nice_ptr = kallsyms_lookup_name_wrapper("can_nice");
|
||||
return can_nice_ptr(p, nice);
|
||||
return (int)deps_call(can_nice_ptr, p, nice);
|
||||
}
|
||||
|
||||
static void (*mmput_async_ptr)(struct mm_struct *mm) = NULL;
|
||||
@ -132,7 +150,7 @@ void mmput_async(struct mm_struct *mm)
|
||||
{
|
||||
if (!mmput_async_ptr)
|
||||
mmput_async_ptr = kallsyms_lookup_name_wrapper("mmput_async");
|
||||
return mmput_async_ptr(mm);
|
||||
deps_call(mmput_async_ptr, mm);
|
||||
}
|
||||
|
||||
static int (*security_binder_set_context_mgr_ptr)(struct task_struct *mgr) = NULL;
|
||||
@ -141,7 +159,7 @@ int security_binder_set_context_mgr(struct task_struct *mgr)
|
||||
{
|
||||
if (!security_binder_set_context_mgr_ptr)
|
||||
security_binder_set_context_mgr_ptr = kallsyms_lookup_name_wrapper("security_binder_set_context_mgr");
|
||||
return security_binder_set_context_mgr_ptr(mgr);
|
||||
return (int)deps_call(security_binder_set_context_mgr_ptr, mgr);
|
||||
}
|
||||
|
||||
static int (*security_binder_transaction_ptr)(struct task_struct *from, struct task_struct *to) = NULL;
|
||||
@ -150,7 +168,7 @@ int security_binder_transaction(struct task_struct *from, struct task_struct *to
|
||||
{
|
||||
if (!security_binder_transaction_ptr)
|
||||
security_binder_transaction_ptr = kallsyms_lookup_name_wrapper("security_binder_transaction");
|
||||
return security_binder_transaction_ptr(from, to);
|
||||
return (int)deps_call(security_binder_transaction_ptr, from, to);
|
||||
}
|
||||
|
||||
static int (*security_binder_transfer_binder_ptr)(struct task_struct *from, struct task_struct *to) = NULL;
|
||||
@ -159,7 +177,7 @@ int security_binder_transfer_binder(struct task_struct *from, struct task_struct
|
||||
{
|
||||
if (!security_binder_transfer_binder_ptr)
|
||||
security_binder_transfer_binder_ptr = kallsyms_lookup_name_wrapper("security_binder_transfer_binder");
|
||||
return security_binder_transfer_binder_ptr(from, to);
|
||||
return (int)deps_call(security_binder_transfer_binder_ptr, from, to);
|
||||
}
|
||||
|
||||
static int (*security_binder_transfer_file_ptr)(struct task_struct *from, struct task_struct *to, struct file *file) = NULL;
|
||||
@ -168,7 +186,7 @@ int security_binder_transfer_file(struct task_struct *from, struct task_struct *
|
||||
{
|
||||
if (!security_binder_transfer_file_ptr)
|
||||
security_binder_transfer_file_ptr = kallsyms_lookup_name_wrapper("security_binder_transfer_file");
|
||||
return security_binder_transfer_file_ptr(from, to, file);
|
||||
return (int)deps_call(security_binder_transfer_file_ptr, from, to, file);
|
||||
}
|
||||
|
||||
static int (*task_work_add_ptr)(struct task_struct *task, struct callback_head *work,
|
||||
@ -179,7 +197,7 @@ int task_work_add(struct task_struct *task, struct callback_head *work,
|
||||
{
|
||||
if (!task_work_add_ptr)
|
||||
task_work_add_ptr = kallsyms_lookup_name_wrapper("task_work_add");
|
||||
return task_work_add_ptr(task, work, notify);
|
||||
return (int)deps_call(task_work_add_ptr, task, work, notify);
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0))
|
||||
@ -189,7 +207,7 @@ void zap_page_range_single(struct vm_area_struct *vma, unsigned long address, un
|
||||
{
|
||||
if (!zap_page_range_single_ptr)
|
||||
zap_page_range_single_ptr = kallsyms_lookup_name_wrapper("zap_page_range_single");
|
||||
zap_page_range_single_ptr(vma, address, size, details);
|
||||
deps_call(zap_page_range_single_ptr, vma, address, size, details);
|
||||
}
|
||||
#else
|
||||
static void (*zap_page_range_ptr)(struct vm_area_struct *, unsigned long, unsigned long) = NULL;
|
||||
@ -198,7 +216,7 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long address, unsigned
|
||||
{
|
||||
if (!zap_page_range_ptr)
|
||||
zap_page_range_ptr = kallsyms_lookup_name_wrapper("zap_page_range");
|
||||
zap_page_range_ptr(vma, address, size);
|
||||
deps_call(zap_page_range_ptr, vma, address, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -208,7 +226,7 @@ void put_ipc_ns(struct ipc_namespace *ns)
|
||||
{
|
||||
if (!put_ipc_ns_ptr)
|
||||
put_ipc_ns_ptr = kallsyms_lookup_name_wrapper("put_ipc_ns");
|
||||
put_ipc_ns_ptr(ns);
|
||||
deps_call(put_ipc_ns_ptr, ns);
|
||||
}
|
||||
|
||||
static struct ipc_namespace *init_ipc_ns_ptr = NULL;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user