tracing/kprobes: handle mixed kernel/userspace probes better
Instead of using the dangerous probe_kernel_read and strncpy_from_unsafe helpers, rework probes to try a user probe based on the address if the architecture has a common address space for kernel and userspace. [svens@linux.ibm.com:use strncpy_from_kernel_nofault() in fetch_store_string()] Link: http://lkml.kernel.org/r/20200606181903.49384-1-svens@linux.ibm.com Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Sven Schnelle <svens@linux.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/20200521152301.2587579-15-hch@lst.de Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Родитель
8d92db5c04
Коммит
9de1fec50b
|
@ -1200,21 +1200,6 @@ static const struct file_operations kprobe_profile_ops = {
|
|||
|
||||
/* Kprobe specific fetch functions */
|
||||
|
||||
/* Return the length of string -- including null terminal byte */
|
||||
static nokprobe_inline int
|
||||
fetch_store_strlen(unsigned long addr)
|
||||
{
|
||||
int ret, len = 0;
|
||||
u8 c;
|
||||
|
||||
do {
|
||||
ret = probe_kernel_read(&c, (u8 *)addr + len, 1);
|
||||
len++;
|
||||
} while (c && ret == 0 && len < MAX_STRING_SIZE);
|
||||
|
||||
return (ret < 0) ? ret : len;
|
||||
}
|
||||
|
||||
/* Return the length of string -- including null terminal byte */
|
||||
static nokprobe_inline int
|
||||
fetch_store_strlen_user(unsigned long addr)
|
||||
|
@ -1224,31 +1209,24 @@ fetch_store_strlen_user(unsigned long addr)
|
|||
return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
|
||||
* length and relative data location.
|
||||
*/
|
||||
/* Return the length of string -- including null terminal byte */
|
||||
static nokprobe_inline int
|
||||
fetch_store_string(unsigned long addr, void *dest, void *base)
|
||||
fetch_store_strlen(unsigned long addr)
|
||||
{
|
||||
int maxlen = get_loc_len(*(u32 *)dest);
|
||||
void *__dest;
|
||||
long ret;
|
||||
int ret, len = 0;
|
||||
u8 c;
|
||||
|
||||
if (unlikely(!maxlen))
|
||||
return -ENOMEM;
|
||||
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
|
||||
if (addr < TASK_SIZE)
|
||||
return fetch_store_strlen_user(addr);
|
||||
#endif
|
||||
|
||||
__dest = get_loc_data(dest, base);
|
||||
do {
|
||||
ret = probe_kernel_read_strict(&c, (u8 *)addr + len, 1);
|
||||
len++;
|
||||
} while (c && ret == 0 && len < MAX_STRING_SIZE);
|
||||
|
||||
/*
|
||||
* Try to get string again, since the string can be changed while
|
||||
* probing.
|
||||
*/
|
||||
ret = strncpy_from_unsafe(__dest, (void *)addr, maxlen);
|
||||
if (ret >= 0)
|
||||
*(u32 *)dest = make_data_loc(ret, __dest - base);
|
||||
|
||||
return ret;
|
||||
return (ret < 0) ? ret : len;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1275,10 +1253,36 @@ fetch_store_string_user(unsigned long addr, void *dest, void *base)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
|
||||
* length and relative data location.
|
||||
*/
|
||||
static nokprobe_inline int
|
||||
probe_mem_read(void *dest, void *src, size_t size)
|
||||
fetch_store_string(unsigned long addr, void *dest, void *base)
|
||||
{
|
||||
return probe_kernel_read(dest, src, size);
|
||||
int maxlen = get_loc_len(*(u32 *)dest);
|
||||
void *__dest;
|
||||
long ret;
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
|
||||
if ((unsigned long)addr < TASK_SIZE)
|
||||
return fetch_store_string_user(addr, dest, base);
|
||||
#endif
|
||||
|
||||
if (unlikely(!maxlen))
|
||||
return -ENOMEM;
|
||||
|
||||
__dest = get_loc_data(dest, base);
|
||||
|
||||
/*
|
||||
* Try to get string again, since the string can be changed while
|
||||
* probing.
|
||||
*/
|
||||
ret = strncpy_from_kernel_nofault(__dest, (void *)addr, maxlen);
|
||||
if (ret >= 0)
|
||||
*(u32 *)dest = make_data_loc(ret, __dest - base);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static nokprobe_inline int
|
||||
|
@ -1289,6 +1293,16 @@ probe_mem_read_user(void *dest, void *src, size_t size)
|
|||
return probe_user_read(dest, uaddr, size);
|
||||
}
|
||||
|
||||
static nokprobe_inline int
|
||||
probe_mem_read(void *dest, void *src, size_t size)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
|
||||
if ((unsigned long)src < TASK_SIZE)
|
||||
return probe_mem_read_user(dest, src, size);
|
||||
#endif
|
||||
return probe_kernel_read_strict(dest, src, size);
|
||||
}
|
||||
|
||||
/* Note that we don't verify it, since the code does not come from user space */
|
||||
static int
|
||||
process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
|
||||
|
|
Загрузка…
Ссылка в новой задаче