/* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h>#include <linux/uaccess.h>#include <linux/kprobes.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/smp.h>#include <linux/debugfs.h>#include <linux/types.h>#include <linux/string.h>#include <linux/ctype.h>#include <linux/ptrace.h>#include <linux/perf_event.h>#include <linux/stringify.h>#include <linux/limits.h>#include <asm/bitsperlong.h> #include "trace.h"#include "trace_output.h" #define MAX_TRACE_ARGS 128#define MAX_ARGSTR_LEN 63#define MAX_EVENT_NAME_LEN 64#define MAX_STRING_SIZE PATH_MAX#define KPROBE_EVENT_SYSTEM "kprobes" /* Reserved field names */#define FIELD_STRING_IP "__probe_ip"#define FIELD_STRING_RETIP "__probe_ret_ip"#define FIELD_STRING_FUNC "__probe_func" const char *reserved_field_names[] = { "common_type", "common_flags", "common_preempt_count", "common_pid", "common_tgid", FIELD_STRING_IP, FIELD_STRING_RETIP, FIELD_STRING_FUNC,}; /* Printing function type */typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *, void *);#define PRINT_TYPE_FUNC_NAME(type) print_type_##type#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type /* Printing in basic type function template */#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ const char *name, \ void *data, void *ent)\{ \ return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\} \static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int)DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int)DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long)DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long)DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int)DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) /* data_rloc: data relative location, compatible with u32 */#define make_data_rloc(len, roffs) \ (((u32)(len) << 16) | ((u32)(roffs) & 0xffff))#define get_rloc_len(dl) ((u32)(dl) >> 16)#define get_rloc_offs(dl) ((u32)(dl) & 0xffff) static inline void *get_rloc_data(u32 *dl){ return (u8 *)dl + get_rloc_offs(*dl);} /* For data_loc conversion */static inline void *get_loc_data(u32 *dl, void *ent){ return (u8 *)ent + get_rloc_offs(*dl);} /* * Convert data_rloc to data_loc: * data_rloc stores the offset from data_rloc itself, but data_loc * stores the offset from event entry. */#define convert_rloc_to_loc(dl, offs) ((u32)(dl) + (offs)) /* For defining macros, define string/string_size types */typedef u32 string;typedef u32 string_size; /* Print type function for string type */static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, const char *name, void *data, void *ent){ int len = *(u32 *)data >> 16; if (!len) return trace_seq_printf(s, " %s=(fault)", name); else return trace_seq_printf(s, " %s=\"%s\"", name, (const char *)get_loc_data(data, ent));}static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; /* Data fetch function type */typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); struct fetch_param { fetch_func_t fn; void *data;}; static __kprobes void call_fetch(struct fetch_param *fprm, struct pt_regs *regs, void *dest){ return fprm->fn(regs, fprm->data, dest);} #define FETCH_FUNC_NAME(method, type) fetch_##method##_##type/* * Define macro for basic types - we don't need to define s* types, because * we have to care only about bitwidth at recording time. */#define DEFINE_BASIC_FETCH_FUNCS(method) \DEFINE_FETCH_##method(u8) \DEFINE_FETCH_##method(u16) \DEFINE_FETCH_##method(u32) \DEFINE_FETCH_##method(u64) #define CHECK_FETCH_FUNCS(method, fn) \ (((FETCH_FUNC_NAME(method, u8) == fn) || \ (FETCH_FUNC_NAME(method, u16) == fn) || \ (FETCH_FUNC_NAME(method, u32) == fn) || \ (FETCH_FUNC_NAME(method, u64) == fn) || \ (FETCH_FUNC_NAME(method, string) == fn) || \ (FETCH_FUNC_NAME(method, string_size) == fn)) \ && (fn != NULL)) /* Data fetch function templates */#define DEFINE_FETCH_reg(type) \static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ void *offset, void *dest) \{ \ *(type *)dest = (type)regs_get_register(regs, \ (unsigned int)((unsigned long)offset)); \}DEFINE_BASIC_FETCH_FUNCS(reg)/* No string on the register */#define fetch_reg_string NULL#define fetch_reg_string_size NULL #define DEFINE_FETCH_stack(type) \static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ void *offset, void *dest) \{ \ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ (unsigned int)((unsigned long)offset)); \}DEFINE_BASIC_FETCH_FUNCS(stack)/* No string on the stack entry */#define fetch_stack_string NULL#define fetch_stack_string_size NULL #define DEFINE_FETCH_retval(type) \static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ void *dummy, void *dest) \{ \ *(type *)dest = (type)regs_return_value(regs); \}DEFINE_BASIC_FETCH_FUNCS(retval)/* No string on the retval */#define fetch_retval_string NULL#define fetch_retval_string_size NULL #define DEFINE_FETCH_memory(type) \static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ void *addr, void *dest) \{ \ type retval; \ if (probe_kernel_address(addr, retval)) \ *(type *)dest = 0; \ else \ *(type *)dest = retval; \}DEFINE_BASIC_FETCH_FUNCS(memory)/* * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max * length and relative data location. */static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, void *addr, void *dest){ long ret; int maxlen = get_rloc_len(*(u32 *)dest); u8 *dst = get_rloc_data(dest); u8 *src = addr; mm_segment_t old_fs = get_fs(); if (!maxlen) return; /* * Try to get string again, since the string can be changed while * probing. */ set_fs(KERNEL_DS); pagefault_disable(); do ret = __copy_from_user_inatomic(dst++, src++, 1); while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen); dst[-1] = '\0'; pagefault_enable(); set_fs(old_fs); if (ret < 0) { /* Failed to fetch string */ ((u8 *)get_rloc_data(dest))[0] = '\0'; *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); } else *(u32 *)dest = make_data_rloc(src - (u8 *)addr, get_rloc_offs(*(u32 *)dest));}/* Return the length of string -- including null terminal byte */static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, void *addr, void *dest){ int ret, len = 0; u8 c; mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); pagefault_disable(); do { ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1); len++; } while (c && ret == 0 && len < MAX_STRING_SIZE); pagefault_enable(); set_fs(old_fs); if (ret < 0) /* Failed to check the length */ *(u32 *)dest = 0; else *(u32 *)dest = len;} /* Memory fetching by symbol */struct symbol_cache { char *symbol; long offset; unsigned long addr;}; static unsigned long update_symbol_cache(struct symbol_cache *sc){ sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); if (sc->addr) sc->addr += sc->offset; return sc->addr;} static void free_symbol_cache(struct symbol_cache *sc){ kfree(sc->symbol); kfree(sc);} static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset){ struct symbol_cache *sc; if (!sym || strlen(sym) == 0) return NULL; sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); if (!sc) return NULL; sc->symbol = kstrdup(sym, GFP_KERNEL); if (!sc->symbol) { kfree(sc); return NULL; } sc->offset = offset; update_symbol_cache(sc); return sc;} #define DEFINE_FETCH_symbol(type) \static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ void *data, void *dest) \{ \ struct symbol_cache *sc = data; \ if (sc->addr) \ fetch_memory_##type(regs, (void *)sc->addr, dest); \ else \ *(type *)dest = 0; \}DEFINE_BASIC_FETCH_FUNCS(symbol)DEFINE_FETCH_symbol(string)DEFINE_FETCH_symbol(string_size) /* Dereference memory access function */struct deref_fetch_param { struct fetch_param orig; long offset;}; #define DEFINE_FETCH_deref(type) \static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ void *data, void *dest) \{ \ struct deref_fetch_param *dprm = data; \ unsigned long addr; \ call_fetch(&dprm->orig, regs, &addr); \ if (addr) { \ addr += dprm->offset; \ fetch_memory_##type(regs, (void *)addr, dest); \ } else \ *(type *)dest = 0; \}DEFINE_BASIC_FETCH_FUNCS(deref)DEFINE_FETCH_deref(string)DEFINE_FETCH_deref(string_size) static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data){ if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) update_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) update_symbol_cache(data->orig.data);} static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data){ if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) free_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) free_symbol_cache(data->orig.data); kfree(data);} /* Bitfield fetch function */struct bitfield_fetch_param { struct fetch_param orig; unsigned char hi_shift; unsigned char low_shift;}; #define DEFINE_FETCH_bitfield(type) \static __kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,\ void *data, void *dest) \{ \ struct bitfield_fetch_param *bprm = data; \ type buf = 0; \ call_fetch(&bprm->orig, regs, &buf); \ if (buf) { \ buf <<= bprm->hi_shift; \ buf >>= bprm->low_shift; \ } \ *(type *)dest = buf; \}DEFINE_BASIC_FETCH_FUNCS(bitfield)#define fetch_bitfield_string NULL#define fetch_bitfield_string_size NULL static __kprobes voidupdate_bitfield_fetch_param(struct bitfield_fetch_param *data){ /* * Don't check the bitfield itself, because this must be the * last fetch function. */ if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) update_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) update_symbol_cache(data->orig.data);} static __kprobes voidfree_bitfield_fetch_param(struct bitfield_fetch_param *data){ /* * Don't check the bitfield itself, because this must be the * last fetch function. */ if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) free_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) free_symbol_cache(data->orig.data); kfree(data);} /* Default (unsigned long) fetch type */#define __DEFAULT_FETCH_TYPE(t) u##t#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) /* Fetch types */enum { FETCH_MTD_reg = 0, FETCH_MTD_stack, FETCH_MTD_retval, FETCH_MTD_memory, FETCH_MTD_symbol, FETCH_MTD_deref, FETCH_MTD_bitfield, FETCH_MTD_END,}; #define ASSIGN_FETCH_FUNC(method, type) \ [FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) #define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ {.name = _name, \ .size = _size, \ .is_signed = sign, \ .print = PRINT_TYPE_FUNC_NAME(ptype), \ .fmt = PRINT_TYPE_FMT_NAME(ptype), \ .fmttype = _fmttype, \ .fetch = { \ASSIGN_FETCH_FUNC(reg, ftype), \ASSIGN_FETCH_FUNC(stack, ftype), \ASSIGN_FETCH_FUNC(retval, ftype), \ASSIGN_FETCH_FUNC(memory, ftype), \ASSIGN_FETCH_FUNC(symbol, ftype), \ASSIGN_FETCH_FUNC(deref, ftype), \ASSIGN_FETCH_FUNC(bitfield, ftype), \ } \ } #define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) #define FETCH_TYPE_STRING 0#define FETCH_TYPE_STRSIZE 1 /* Fetch type information table */static const struct fetch_type { const char *name; /* Name of type */ size_t size; /* Byte size of type */ int is_signed; /* Signed flag */ print_type_func_t print; /* Print functions */ const char *fmt; /* Fromat string */ const char *fmttype; /* Name in format file */ /* Fetch functions */ fetch_func_t fetch[FETCH_MTD_END];} fetch_type_table[] = { /* Special types */ [FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, "__data_loc char[]"), [FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32, string_size, sizeof(u32), 0, "u32"), /* Basic types */ ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1),}; static const struct fetch_type *find_fetch_type(const char *type){ int i; if (!type) type = DEFAULT_FETCH_TYPE_STR; /* Special case: bitfield */ if (*type == 'b') { unsigned long bs; type = strchr(type, '/'); if (!type) goto fail; type++; if (strict_strtoul(type, 0, &bs))Lines 1–500 of 2121. Load more
/* * Kprobes-based tracing events * * Created by Masami Hiramatsu <mhiramat@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h> #include <linux/uaccess.h> #include <linux/kprobes.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/smp.h> #include <linux/debugfs.h> #include <linux/types.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/ptrace.h> #include <linux/perf_event.h> #include <linux/stringify.h> #include <linux/limits.h> #include <asm/bitsperlong.h> #include "trace.h" #include "trace_output.h" #define MAX_TRACE_ARGS 128 #define MAX_ARGSTR_LEN 63 #define MAX_EVENT_NAME_LEN 64 #define MAX_STRING_SIZE PATH_MAX #define KPROBE_EVENT_SYSTEM "kprobes" /* Reserved field names */ #define FIELD_STRING_IP "__probe_ip" #define FIELD_STRING_RETIP "__probe_ret_ip" #define FIELD_STRING_FUNC "__probe_func" const char *reserved_field_names[] = { "common_type", "common_flags", "common_preempt_count", "common_pid", "common_tgid", FIELD_STRING_IP, FIELD_STRING_RETIP, FIELD_STRING_FUNC, }; /* Printing function type */ typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *, void *); #define PRINT_TYPE_FUNC_NAME(type) print_type_##type #define PRINT_TYPE_FMT_NAME(type) print_type_format_##type /* Printing in basic type function template */ #define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \ static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ const char *name, \ void *data, void *ent)\ { \ return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\ } \ static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int) DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int) DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long) DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long) DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int) DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int) DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long) DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) /* data_rloc: data relative location, compatible with u32 */ #define make_data_rloc(len, roffs) \ (((u32)(len) << 16) | ((u32)(roffs) & 0xffff)) #define get_rloc_len(dl) ((u32)(dl) >> 16) #define get_rloc_offs(dl) ((u32)(dl) & 0xffff) static inline void *get_rloc_data(u32 *dl) { return (u8 *)dl + get_rloc_offs(*dl); } /* For data_loc conversion */ static inline void *get_loc_data(u32 *dl, void *ent) { return (u8 *)ent + get_rloc_offs(*dl); } /* * Convert data_rloc to data_loc: * data_rloc stores the offset from data_rloc itself, but data_loc * stores the offset from event entry. */ #define convert_rloc_to_loc(dl, offs) ((u32)(dl) + (offs)) /* For defining macros, define string/string_size types */ typedef u32 string; typedef u32 string_size; /* Print type function for string type */ static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, const char *name, void *data, void *ent) { int len = *(u32 *)data >> 16; if (!len) return trace_seq_printf(s, " %s=(fault)", name); else return trace_seq_printf(s, " %s=\"%s\"", name, (const char *)get_loc_data(data, ent)); } static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; /* Data fetch function type */ typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); struct fetch_param { fetch_func_t fn; void *data; }; static __kprobes void call_fetch(struct fetch_param *fprm, struct pt_regs *regs, void *dest) { return fprm->fn(regs, fprm->data, dest); } #define FETCH_FUNC_NAME(method, type) fetch_##method##_##type /* * Define macro for basic types - we don't need to define s* types, because * we have to care only about bitwidth at recording time. */ #define DEFINE_BASIC_FETCH_FUNCS(method) \ DEFINE_FETCH_##method(u8) \ DEFINE_FETCH_##method(u16) \ DEFINE_FETCH_##method(u32) \ DEFINE_FETCH_##method(u64) #define CHECK_FETCH_FUNCS(method, fn) \ (((FETCH_FUNC_NAME(method, u8) == fn) || \ (FETCH_FUNC_NAME(method, u16) == fn) || \ (FETCH_FUNC_NAME(method, u32) == fn) || \ (FETCH_FUNC_NAME(method, u64) == fn) || \ (FETCH_FUNC_NAME(method, string) == fn) || \ (FETCH_FUNC_NAME(method, string_size) == fn)) \ && (fn != NULL)) /* Data fetch function templates */ #define DEFINE_FETCH_reg(type) \ static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ void *offset, void *dest) \ { \ *(type *)dest = (type)regs_get_register(regs, \ (unsigned int)((unsigned long)offset)); \ } DEFINE_BASIC_FETCH_FUNCS(reg) /* No string on the register */ #define fetch_reg_string NULL #define fetch_reg_string_size NULL #define DEFINE_FETCH_stack(type) \ static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ void *offset, void *dest) \ { \ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ (unsigned int)((unsigned long)offset)); \ } DEFINE_BASIC_FETCH_FUNCS(stack) /* No string on the stack entry */ #define fetch_stack_string NULL #define fetch_stack_string_size NULL #define DEFINE_FETCH_retval(type) \ static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ void *dummy, void *dest) \ { \ *(type *)dest = (type)regs_return_value(regs); \ } DEFINE_BASIC_FETCH_FUNCS(retval) /* No string on the retval */ #define fetch_retval_string NULL #define fetch_retval_string_size NULL #define DEFINE_FETCH_memory(type) \ static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ void *addr, void *dest) \ { \ type retval; \ if (probe_kernel_address(addr, retval)) \ *(type *)dest = 0; \ else \ *(type *)dest = retval; \ } DEFINE_BASIC_FETCH_FUNCS(memory) /* * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max * length and relative data location. */ static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs, void *addr, void *dest) { long ret; int maxlen = get_rloc_len(*(u32 *)dest); u8 *dst = get_rloc_data(dest); u8 *src = addr; mm_segment_t old_fs = get_fs(); if (!maxlen) return; /* * Try to get string again, since the string can be changed while * probing. */ set_fs(KERNEL_DS); pagefault_disable(); do ret = __copy_from_user_inatomic(dst++, src++, 1); while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen); dst[-1] = '\0'; pagefault_enable(); set_fs(old_fs); if (ret < 0) { /* Failed to fetch string */ ((u8 *)get_rloc_data(dest))[0] = '\0'; *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest)); } else *(u32 *)dest = make_data_rloc(src - (u8 *)addr, get_rloc_offs(*(u32 *)dest)); } /* Return the length of string -- including null terminal byte */ static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs, void *addr, void *dest) { int ret, len = 0; u8 c; mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); pagefault_disable(); do { ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1); len++; } while (c && ret == 0 && len < MAX_STRING_SIZE); pagefault_enable(); set_fs(old_fs); if (ret < 0) /* Failed to check the length */ *(u32 *)dest = 0; else *(u32 *)dest = len; } /* Memory fetching by symbol */ struct symbol_cache { char *symbol; long offset; unsigned long addr; }; static unsigned long update_symbol_cache(struct symbol_cache *sc) { sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol); if (sc->addr) sc->addr += sc->offset; return sc->addr; } static void free_symbol_cache(struct symbol_cache *sc) { kfree(sc->symbol); kfree(sc); } static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) { struct symbol_cache *sc; if (!sym || strlen(sym) == 0) return NULL; sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL); if (!sc) return NULL; sc->symbol = kstrdup(sym, GFP_KERNEL); if (!sc->symbol) { kfree(sc); return NULL; } sc->offset = offset; update_symbol_cache(sc); return sc; } #define DEFINE_FETCH_symbol(type) \ static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ void *data, void *dest) \ { \ struct symbol_cache *sc = data; \ if (sc->addr) \ fetch_memory_##type(regs, (void *)sc->addr, dest); \ else \ *(type *)dest = 0; \ } DEFINE_BASIC_FETCH_FUNCS(symbol) DEFINE_FETCH_symbol(string) DEFINE_FETCH_symbol(string_size) /* Dereference memory access function */ struct deref_fetch_param { struct fetch_param orig; long offset; }; #define DEFINE_FETCH_deref(type) \ static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ void *data, void *dest) \ { \ struct deref_fetch_param *dprm = data; \ unsigned long addr; \ call_fetch(&dprm->orig, regs, &addr); \ if (addr) { \ addr += dprm->offset; \ fetch_memory_##type(regs, (void *)addr, dest); \ } else \ *(type *)dest = 0; \ } DEFINE_BASIC_FETCH_FUNCS(deref) DEFINE_FETCH_deref(string) DEFINE_FETCH_deref(string_size) static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data) { if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) update_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) update_symbol_cache(data->orig.data); } static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) { if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) free_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) free_symbol_cache(data->orig.data); kfree(data); } /* Bitfield fetch function */ struct bitfield_fetch_param { struct fetch_param orig; unsigned char hi_shift; unsigned char low_shift; }; #define DEFINE_FETCH_bitfield(type) \ static __kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,\ void *data, void *dest) \ { \ struct bitfield_fetch_param *bprm = data; \ type buf = 0; \ call_fetch(&bprm->orig, regs, &buf); \ if (buf) { \ buf <<= bprm->hi_shift; \ buf >>= bprm->low_shift; \ } \ *(type *)dest = buf; \ } DEFINE_BASIC_FETCH_FUNCS(bitfield) #define fetch_bitfield_string NULL #define fetch_bitfield_string_size NULL static __kprobes void update_bitfield_fetch_param(struct bitfield_fetch_param *data) { /* * Don't check the bitfield itself, because this must be the * last fetch function. */ if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) update_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) update_symbol_cache(data->orig.data); } static __kprobes void free_bitfield_fetch_param(struct bitfield_fetch_param *data) { /* * Don't check the bitfield itself, because this must be the * last fetch function. */ if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) free_deref_fetch_param(data->orig.data); else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) free_symbol_cache(data->orig.data); kfree(data); } /* Default (unsigned long) fetch type */ #define __DEFAULT_FETCH_TYPE(t) u##t #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) #define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) #define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) /* Fetch types */ enum { FETCH_MTD_reg = 0, FETCH_MTD_stack, FETCH_MTD_retval, FETCH_MTD_memory, FETCH_MTD_symbol, FETCH_MTD_deref, FETCH_MTD_bitfield, FETCH_MTD_END, }; #define ASSIGN_FETCH_FUNC(method, type) \ [FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) #define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ {.name = _name, \ .size = _size, \ .is_signed = sign, \ .print = PRINT_TYPE_FUNC_NAME(ptype), \ .fmt = PRINT_TYPE_FMT_NAME(ptype), \ .fmttype = _fmttype, \ .fetch = { \ ASSIGN_FETCH_FUNC(reg, ftype), \ ASSIGN_FETCH_FUNC(stack, ftype), \ ASSIGN_FETCH_FUNC(retval, ftype), \ ASSIGN_FETCH_FUNC(memory, ftype), \ ASSIGN_FETCH_FUNC(symbol, ftype), \ ASSIGN_FETCH_FUNC(deref, ftype), \ ASSIGN_FETCH_FUNC(bitfield, ftype), \ } \ } #define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ __ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) #define FETCH_TYPE_STRING 0 #define FETCH_TYPE_STRSIZE 1 /* Fetch type information table */ static const struct fetch_type { const char *name; /* Name of type */ size_t size; /* Byte size of type */ int is_signed; /* Signed flag */ print_type_func_t print; /* Print functions */ const char *fmt; /* Fromat string */ const char *fmttype; /* Name in format file */ /* Fetch functions */ fetch_func_t fetch[FETCH_MTD_END]; } fetch_type_table[] = { /* Special types */ [FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, "__data_loc char[]"), [FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32, string_size, sizeof(u32), 0, "u32"), /* Basic types */ ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u32, u32, 0), ASSIGN_FETCH_TYPE(u64, u64, 0), ASSIGN_FETCH_TYPE(s8, u8, 1), ASSIGN_FETCH_TYPE(s16, u16, 1), ASSIGN_FETCH_TYPE(s32, u32, 1), ASSIGN_FETCH_TYPE(s64, u64, 1), }; static const struct fetch_type *find_fetch_type(const char *type) { int i; if (!type) type = DEFAULT_FETCH_TYPE_STR; /* Special case: bitfield */ if (*type == 'b') { unsigned long bs; type = strchr(type, '/'); if (!type) goto fail; type++; if (strict_strtoul(type, 0, &bs))