mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	tracing/probes: Implement 'memory' fetch method for uprobes
Use separate method to fetch from memory. Move existing functions to trace_kprobe.c and make them static. Also add new memory fetch implementation for uprobes. Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Acked-by: Oleg Nesterov <oleg@redhat.com> Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com> Cc: zhangwei(Jovi) <jovi.zhangwei@huawei.com> Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
		
							parent
							
								
									3925f4a5af
								
							
						
					
					
						commit
						5baaa59ef0
					
				
					 4 changed files with 129 additions and 81 deletions
				
			
		| 
						 | 
				
			
			@ -148,6 +148,83 @@ DEFINE_BASIC_FETCH_FUNCS(stack)
 | 
			
		|||
#define fetch_stack_string	NULL
 | 
			
		||||
#define fetch_stack_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)
 | 
			
		||||
{
 | 
			
		||||
	mm_segment_t old_fs;
 | 
			
		||||
	int ret, len = 0;
 | 
			
		||||
	u8 c;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define DEFINE_FETCH_symbol(type)					\
 | 
			
		||||
__kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,	\
 | 
			
		||||
					  void *data, void *dest)	\
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,83 +103,6 @@ DEFINE_BASIC_FETCH_FUNCS(retval)
 | 
			
		|||
#define fetch_retval_string		NULL
 | 
			
		||||
#define fetch_retval_string_size	NULL
 | 
			
		||||
 | 
			
		||||
#define DEFINE_FETCH_memory(type)					\
 | 
			
		||||
__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.
 | 
			
		||||
 */
 | 
			
		||||
__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 */
 | 
			
		||||
__kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
 | 
			
		||||
							void *addr, void *dest)
 | 
			
		||||
{
 | 
			
		||||
	mm_segment_t old_fs;
 | 
			
		||||
	int ret, len = 0;
 | 
			
		||||
	u8 c;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Dereference memory access function */
 | 
			
		||||
struct deref_fetch_param {
 | 
			
		||||
	struct fetch_param	orig;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -171,10 +171,6 @@ DECLARE_BASIC_FETCH_FUNCS(retval);
 | 
			
		|||
#define fetch_retval_string			NULL
 | 
			
		||||
#define fetch_retval_string_size		NULL
 | 
			
		||||
 | 
			
		||||
DECLARE_BASIC_FETCH_FUNCS(memory);
 | 
			
		||||
DECLARE_FETCH_FUNC(memory, string);
 | 
			
		||||
DECLARE_FETCH_FUNC(memory, string_size);
 | 
			
		||||
 | 
			
		||||
DECLARE_BASIC_FETCH_FUNCS(symbol);
 | 
			
		||||
DECLARE_FETCH_FUNC(symbol, string);
 | 
			
		||||
DECLARE_FETCH_FUNC(symbol, string_size);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,6 +114,58 @@ DEFINE_BASIC_FETCH_FUNCS(stack)
 | 
			
		|||
#define fetch_stack_string	NULL
 | 
			
		||||
#define fetch_stack_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;							\
 | 
			
		||||
	void __user *vaddr = (void __force __user *) addr;		\
 | 
			
		||||
									\
 | 
			
		||||
	if (copy_from_user(&retval, vaddr, sizeof(type)))		\
 | 
			
		||||
		*(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;
 | 
			
		||||
	u32 rloc = *(u32 *)dest;
 | 
			
		||||
	int maxlen  = get_rloc_len(rloc);
 | 
			
		||||
	u8 *dst = get_rloc_data(dest);
 | 
			
		||||
	void __user *src = (void __force __user *) addr;
 | 
			
		||||
 | 
			
		||||
	if (!maxlen)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ret = strncpy_from_user(dst, src, maxlen);
 | 
			
		||||
 | 
			
		||||
	if (ret < 0) {	/* Failed to fetch string */
 | 
			
		||||
		((u8 *)get_rloc_data(dest))[0] = '\0';
 | 
			
		||||
		*(u32 *)dest = make_data_rloc(0, get_rloc_offs(rloc));
 | 
			
		||||
	} else {
 | 
			
		||||
		*(u32 *)dest = make_data_rloc(ret, get_rloc_offs(rloc));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
 | 
			
		||||
						      void *addr, void *dest)
 | 
			
		||||
{
 | 
			
		||||
	int len;
 | 
			
		||||
	void __user *vaddr = (void __force __user *) addr;
 | 
			
		||||
 | 
			
		||||
	len = strnlen_user(vaddr, MAX_STRING_SIZE);
 | 
			
		||||
 | 
			
		||||
	if (len == 0 || len > MAX_STRING_SIZE)  /* Failed to check length */
 | 
			
		||||
		*(u32 *)dest = 0;
 | 
			
		||||
	else
 | 
			
		||||
		*(u32 *)dest = len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* uprobes do not support symbol fetch methods */
 | 
			
		||||
#define fetch_symbol_u8			NULL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue