tracing: Move duplicate code of trace_kprobe/eprobe.c into header

The functions:

  fetch_store_strlen_user()
  fetch_store_strlen()
  fetch_store_string_user()
  fetch_store_string()

are identical in both trace_kprobe.c and trace_eprobe.c. Move them into
a new header file trace_probe_kernel.h to share it. This code will later
be used by the synthetic events as well.

Marked for stable as a fix for a crash in synthetic events requires it.

Link: https://lkml.kernel.org/r/20221012104534.467668078@goodmis.org

Cc: stable@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Tom Zanussi <zanussi@kernel.org>
Acked-by: default avatarMasami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: default avatarTom Zanussi <zanussi@kernel.org>
Fixes: bd82631d ("tracing: Add support for dynamic strings to synthetic events")
Signed-off-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
parent b7085b6f
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "trace_dynevent.h" #include "trace_dynevent.h"
#include "trace_probe.h" #include "trace_probe.h"
#include "trace_probe_tmpl.h" #include "trace_probe_tmpl.h"
#include "trace_probe_kernel.h"
#define EPROBE_EVENT_SYSTEM "eprobes" #define EPROBE_EVENT_SYSTEM "eprobes"
...@@ -456,29 +457,14 @@ NOKPROBE_SYMBOL(process_fetch_insn) ...@@ -456,29 +457,14 @@ NOKPROBE_SYMBOL(process_fetch_insn)
static nokprobe_inline int static nokprobe_inline int
fetch_store_strlen_user(unsigned long addr) fetch_store_strlen_user(unsigned long addr)
{ {
const void __user *uaddr = (__force const void __user *)addr; return kern_fetch_store_strlen_user(addr);
return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);
} }
/* Return the length of string -- including null terminal byte */ /* Return the length of string -- including null terminal byte */
static nokprobe_inline int static nokprobe_inline int
fetch_store_strlen(unsigned long addr) fetch_store_strlen(unsigned long addr)
{ {
int ret, len = 0; return kern_fetch_store_strlen(addr);
u8 c;
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if (addr < TASK_SIZE)
return fetch_store_strlen_user(addr);
#endif
do {
ret = copy_from_kernel_nofault(&c, (u8 *)addr + len, 1);
len++;
} while (c && ret == 0 && len < MAX_STRING_SIZE);
return (ret < 0) ? ret : len;
} }
/* /*
...@@ -488,21 +474,7 @@ fetch_store_strlen(unsigned long addr) ...@@ -488,21 +474,7 @@ fetch_store_strlen(unsigned long addr)
static nokprobe_inline int static nokprobe_inline int
fetch_store_string_user(unsigned long addr, void *dest, void *base) fetch_store_string_user(unsigned long addr, void *dest, void *base)
{ {
const void __user *uaddr = (__force const void __user *)addr; return kern_fetch_store_string_user(addr, dest, base);
int maxlen = get_loc_len(*(u32 *)dest);
void *__dest;
long ret;
if (unlikely(!maxlen))
return -ENOMEM;
__dest = get_loc_data(dest, base);
ret = strncpy_from_user_nofault(__dest, uaddr, maxlen);
if (ret >= 0)
*(u32 *)dest = make_data_loc(ret, __dest - base);
return ret;
} }
/* /*
...@@ -512,29 +484,7 @@ fetch_store_string_user(unsigned long addr, void *dest, void *base) ...@@ -512,29 +484,7 @@ fetch_store_string_user(unsigned long addr, void *dest, void *base)
static nokprobe_inline int static nokprobe_inline int
fetch_store_string(unsigned long addr, void *dest, void *base) fetch_store_string(unsigned long addr, void *dest, void *base)
{ {
int maxlen = get_loc_len(*(u32 *)dest); return kern_fetch_store_string(addr, dest, base);
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 static nokprobe_inline int
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "trace_kprobe_selftest.h" #include "trace_kprobe_selftest.h"
#include "trace_probe.h" #include "trace_probe.h"
#include "trace_probe_tmpl.h" #include "trace_probe_tmpl.h"
#include "trace_probe_kernel.h"
#define KPROBE_EVENT_SYSTEM "kprobes" #define KPROBE_EVENT_SYSTEM "kprobes"
#define KRETPROBE_MAXACTIVE_MAX 4096 #define KRETPROBE_MAXACTIVE_MAX 4096
...@@ -1223,29 +1224,14 @@ static const struct file_operations kprobe_profile_ops = { ...@@ -1223,29 +1224,14 @@ static const struct file_operations kprobe_profile_ops = {
static nokprobe_inline int static nokprobe_inline int
fetch_store_strlen_user(unsigned long addr) fetch_store_strlen_user(unsigned long addr)
{ {
const void __user *uaddr = (__force const void __user *)addr; return kern_fetch_store_strlen_user(addr);
return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);
} }
/* Return the length of string -- including null terminal byte */ /* Return the length of string -- including null terminal byte */
static nokprobe_inline int static nokprobe_inline int
fetch_store_strlen(unsigned long addr) fetch_store_strlen(unsigned long addr)
{ {
int ret, len = 0; return kern_fetch_store_strlen(addr);
u8 c;
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if (addr < TASK_SIZE)
return fetch_store_strlen_user(addr);
#endif
do {
ret = copy_from_kernel_nofault(&c, (u8 *)addr + len, 1);
len++;
} while (c && ret == 0 && len < MAX_STRING_SIZE);
return (ret < 0) ? ret : len;
} }
/* /*
...@@ -1255,21 +1241,7 @@ fetch_store_strlen(unsigned long addr) ...@@ -1255,21 +1241,7 @@ fetch_store_strlen(unsigned long addr)
static nokprobe_inline int static nokprobe_inline int
fetch_store_string_user(unsigned long addr, void *dest, void *base) fetch_store_string_user(unsigned long addr, void *dest, void *base)
{ {
const void __user *uaddr = (__force const void __user *)addr; return kern_fetch_store_string_user(addr, dest, base);
int maxlen = get_loc_len(*(u32 *)dest);
void *__dest;
long ret;
if (unlikely(!maxlen))
return -ENOMEM;
__dest = get_loc_data(dest, base);
ret = strncpy_from_user_nofault(__dest, uaddr, maxlen);
if (ret >= 0)
*(u32 *)dest = make_data_loc(ret, __dest - base);
return ret;
} }
/* /*
...@@ -1279,29 +1251,7 @@ fetch_store_string_user(unsigned long addr, void *dest, void *base) ...@@ -1279,29 +1251,7 @@ fetch_store_string_user(unsigned long addr, void *dest, void *base)
static nokprobe_inline int static nokprobe_inline int
fetch_store_string(unsigned long addr, void *dest, void *base) fetch_store_string(unsigned long addr, void *dest, void *base)
{ {
int maxlen = get_loc_len(*(u32 *)dest); return kern_fetch_store_string(addr, dest, base);
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 static nokprobe_inline int
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __TRACE_PROBE_KERNEL_H_
#define __TRACE_PROBE_KERNEL_H_
/*
* This depends on trace_probe.h, but can not include it due to
* the way trace_probe_tmpl.h is used by trace_kprobe.c and trace_eprobe.c.
* Which means that any other user must include trace_probe.h before including
* this file.
*/
/* Return the length of string -- including null terminal byte */
static nokprobe_inline int
kern_fetch_store_strlen_user(unsigned long addr)
{
const void __user *uaddr = (__force const void __user *)addr;
return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);
}
/* Return the length of string -- including null terminal byte */
static nokprobe_inline int
kern_fetch_store_strlen(unsigned long addr)
{
int ret, len = 0;
u8 c;
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if (addr < TASK_SIZE)
return kern_fetch_store_strlen_user(addr);
#endif
do {
ret = copy_from_kernel_nofault(&c, (u8 *)addr + len, 1);
len++;
} while (c && ret == 0 && len < MAX_STRING_SIZE);
return (ret < 0) ? ret : len;
}
/*
* Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf
* with max length and relative data location.
*/
static nokprobe_inline int
kern_fetch_store_string_user(unsigned long addr, void *dest, void *base)
{
const void __user *uaddr = (__force const void __user *)addr;
int maxlen = get_loc_len(*(u32 *)dest);
void *__dest;
long ret;
if (unlikely(!maxlen))
return -ENOMEM;
__dest = get_loc_data(dest, base);
ret = strncpy_from_user_nofault(__dest, uaddr, maxlen);
if (ret >= 0)
*(u32 *)dest = make_data_loc(ret, __dest - base);
return ret;
}
/*
* Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
* length and relative data location.
*/
static nokprobe_inline int
kern_fetch_store_string(unsigned long addr, void *dest, void *base)
{
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 kern_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;
}
#endif /* __TRACE_PROBE_KERNEL_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment