커널 코드 영역 확인(func_ptr_is_kernel_text())

 

 

func_ptr_is_kernel_text()

kernel/extable.c

/*
 * On some architectures (PPC64, IA64) function pointers
 * are actually only tokens to some data that then holds the
 * real function address. As a result, to find if a function
 * pointer is part of the kernel text, we need to do some
 * special dereferencing first.
 */
int func_ptr_is_kernel_text(void *ptr)
{
        unsigned long addr;
        addr = (unsigned long) dereference_function_descriptor(ptr);
        if (core_kernel_text(addr))
                return 1;
        return is_module_text_address(addr);
}

ptr 주소가 커널 코드 또는 모듈 코드 영역인지 여부를 알아온다.

 

__kernel_text_address()

kernel/extable.c

int __kernel_text_address(unsigned long addr)
{
        if (core_kernel_text(addr))
                return 1;
        if (is_module_text_address(addr))
                return 1;
        if (is_ftrace_trampoline(addr))
                return 1;
        /*
         * There might be init symbols in saved stacktraces.
         * Give those symbols a chance to be printed in
         * backtraces (such as lockdep traces).
         *
         * Since we are after the module-symbols check, there's
         * no danger of address overlap:
         */
        if (init_kernel_text(addr))
                return 1;
        return 0;
}

addr 주소가 core 커널 코드, 모듈 코드, ftrace 코드, init 커널 코드 영역인 경우 1을 리턴하고 그렇지 않으면 0을 리턴한다.

 

core_kernel_text()

kernel/extable.c

int core_kernel_text(unsigned long addr)
{
        if (addr >= (unsigned long)_stext &&
            addr < (unsigned long)_etext)
                return 1;

        if (system_state == SYSTEM_BOOTING &&
            init_kernel_text(addr))
                return 1;
        return 0;
}

addr 주소가 _stext ~ _etext 범위에 있거나 부팅 중 init kernel 코드 영역인 경우 1을 리턴하고 그렇지 않으면 0을 리턴한다.

 

core_kernel_data()

kernel/extable.c

/**
 * core_kernel_data - tell if addr points to kernel data
 * @addr: address to test
 *
 * Returns true if @addr passed in is from the core kernel data
 * section.
 *
 * Note: On some archs it may return true for core RODATA, and false
 *  for others. But will always be true for core RW data.
 */
int core_kernel_data(unsigned long addr)
{
        if (addr >= (unsigned long)_sdata &&
            addr < (unsigned long)_edata)
                return 1;
        return 0;
}

addr 주소가 _sdata ~ _edata 영역에 포함되어 있는지 여부를 리턴한다.

 

init_kernel_text()

kernel/extable.c

static inline int init_kernel_text(unsigned long addr)
{
        if (addr >= (unsigned long)_sinittext &&
            addr < (unsigned long)_einittext)
                return 1;
        return 0;
}

addr 주소가 _sinittext ~ _einittext 영역에 포함되어 있는지 여부를 리턴한다.

 

is_module_text_address()

kernel/module.c

/*
 * is_module_text_address - is this address inside module code?
 * @addr: the address to check.
 *
 * See is_module_address() if you simply want to see if the address is
 * anywhere in a module.  See kernel_text_address() for testing if an
 * address corresponds to kernel or module code.
 */
bool is_module_text_address(unsigned long addr)
{
        bool ret;
                                      
        preempt_disable();
        ret = __module_text_address(addr) != NULL;
        preempt_enable();

        return ret;
}

addr 주소가 모듈 코드 영역에 포함되어 있는지 여부를 알아온다.

 

__module_text_address()

kernel/module.c

/*
 * __module_text_address - get the module whose code contains an address.
 * @addr: the address.
 *
 * Must be called with preempt disabled or module mutex held so that
 * module doesn't get freed during this.
 */
struct module *__module_text_address(unsigned long addr)
{
        struct module *mod = __module_address(addr);
        if (mod) {
                /* Make sure it's within the text section. */
                if (!within(addr, mod->module_init, mod->init_text_size)
                    && !within(addr, mod->module_core, mod->core_text_size))
                        mod = NULL;
        }
        return mod;
}
EXPORT_SYMBOL_GPL(__module_text_address);

addr 주소가 모듈 초기화 코드 및 모듈 코어 코드에 포함되어 있는지 여부를 알아온다.

 

__module_address()

kernel/module.c

/*
 * __module_address - get the module which contains an address.
 * @addr: the address.
 *
 * Must be called with preempt disabled or module mutex held so that
 * module doesn't get freed during this.
 */
struct module *__module_address(unsigned long addr)
{
        struct module *mod;

        if (addr < module_addr_min || addr > module_addr_max)
                return NULL;

        list_for_each_entry_rcu(mod, &modules, list) {
                if (mod->state == MODULE_STATE_UNFORMED)
                        continue;
                if (within_module(addr, mod))
                        return mod;
        }
        return NULL;
}
EXPORT_SYMBOL_GPL(__module_address);

addr 주소가 module_addr_min ~ module_addr_max에 포함되어 있고 로드된 모듈의 코드영역에 있는 경우 module 포인터를 리턴하고 그렇지 않으면 null을 리턴한다.

 

within_module()

include/linux/module.h

static inline bool within_module(unsigned long addr, const struct module *mod)
{
        return within_module_init(addr, mod) || within_module_core(addr, mod);
}

addr 주소가 지정된 모듈의 초기화 코드 또는 코어 코드에 포함되어 있는지 여부를 리턴한다.

 

within_module_init()

include/linux/module.h

static inline bool within_module_init(unsigned long addr,
                                      const struct module *mod)
{                       
        return (unsigned long)mod->module_init <= addr &&
               addr < (unsigned long)mod->module_init + mod->init_size;
}

addr 주소가 지정된 모듈의 초기화 코드 영역에 포함되어 있는지 여부를 리턴한다.

 

is_ftrace_trampoline()

kernel/trace/ftrace.c

/*
 * This is used by __kernel_text_address() to return true if the
 * address is on a dynamically allocated trampoline that would
 * not return true for either core_kernel_text() or
 * is_module_text_address().
 */
bool is_ftrace_trampoline(unsigned long addr)
{
        struct ftrace_ops *op;
        bool ret = false;

        /*
         * Some of the ops may be dynamically allocated,
         * they are freed after a synchronize_sched().
         */
        preempt_disable_notrace();

        do_for_each_ftrace_op(op, ftrace_ops_list) {
                /*
                 * This is to check for dynamically allocated trampolines.
                 * Trampolines that are in kernel text will have
                 * core_kernel_text() return true.
                 */
                if (op->trampoline && op->trampoline_size)
                        if (addr >= op->trampoline &&
                            addr < op->trampoline + op->trampoline_size) {
                                ret = true;
                                goto out; 
                        }       
        } while_for_each_ftrace_op(op);
        
 out:   
        preempt_enable_notrace(); 

        return ret;
}

addr 주소가 각 ftrace_ops_list를 뒤져 각 op 구조체의 영역에 포함되어 있는지 여부를 알아내어 리턴한다.

 

do_for_each_ftrace_op()

kernel/trace/ftrace.c

/*
 * Traverse the ftrace_global_list, invoking all entries.  The reason that we
 * can use rcu_dereference_raw_notrace() is that elements removed from this list
 * are simply leaked, so there is no need to interact with a grace-period
 * mechanism.  The rcu_dereference_raw_notrace() calls are needed to handle
 * concurrent insertions into the ftrace_global_list.
 *
 * Silly Alpha and silly pointer-speculation compiler optimizations!
 */
#define do_for_each_ftrace_op(op, list)                 \
        op = rcu_dereference_raw_notrace(list);                 \
        do

 

while_for_each_ftrace_op()

kernel/trace/ftrace.c

/*
 * Optimized for just a single item in the list (as that is the normal case).
 */
#define while_for_each_ftrace_op(op)                            \
        while (likely(op = rcu_dereference_raw_notrace((op)->next)) &&  \
               unlikely((op) != &ftrace_list_end))

 

댓글 남기기