Commit af9b3fa1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-probes-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull trace probes updates from Steven Rostedt:

 - New "symstr" type for dynamic events that writes the name of the
   function+offset into the ring buffer and not just the address

 - Prevent kernel symbol processing on addresses in user space probes
   (uprobes).

 - And minor fixes and clean ups

* tag 'trace-probes-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  tracing/probes: Reject symbol/symstr type for uprobe
  tracing/probes: Add symstr type for dynamic events
  kprobes: kretprobe events missing on 2-core KVM guest
  kprobes: Fix check for probe enabled in kill_kprobe()
  test_kprobes: Fix implicit declaration error of test_kprobes
  tracing: Fix race where eprobes can be called before the event
parents 7a5189c5 d4505aa6
...@@ -131,8 +131,7 @@ For example, if the function is non-recursive and is called with a ...@@ -131,8 +131,7 @@ For example, if the function is non-recursive and is called with a
spinlock held, maxactive = 1 should be enough. If the function is spinlock held, maxactive = 1 should be enough. If the function is
non-recursive and can never relinquish the CPU (e.g., via a semaphore non-recursive and can never relinquish the CPU (e.g., via a semaphore
or preemption), NR_CPUS should be enough. If maxactive <= 0, it is or preemption), NR_CPUS should be enough. If maxactive <= 0, it is
set to a default value. If CONFIG_PREEMPT is enabled, the default set to a default value: max(10, 2*NR_CPUS).
is max(10, 2*NR_CPUS). Otherwise, the default is NR_CPUS.
It's not a disaster if you set maxactive too low; you'll just miss It's not a disaster if you set maxactive too low; you'll just miss
some probes. In the kretprobe struct, the nmissed field is set to some probes. In the kretprobe struct, the nmissed field is set to
......
...@@ -58,8 +58,8 @@ Synopsis of kprobe_events ...@@ -58,8 +58,8 @@ Synopsis of kprobe_events
NAME=FETCHARG : Set NAME as the argument name of FETCHARG. NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
(x8/x16/x32/x64), "string", "ustring" and bitfield (x8/x16/x32/x64), "string", "ustring", "symbol", "symstr"
are supported. and bitfield are supported.
(\*1) only for the probe on function entry (offs == 0). (\*1) only for the probe on function entry (offs == 0).
(\*2) only for return probe. (\*2) only for return probe.
...@@ -96,6 +96,10 @@ offset, and container-size (usually 32). The syntax is:: ...@@ -96,6 +96,10 @@ offset, and container-size (usually 32). The syntax is::
Symbol type('symbol') is an alias of u32 or u64 type (depends on BITS_PER_LONG) Symbol type('symbol') is an alias of u32 or u64 type (depends on BITS_PER_LONG)
which shows given pointer in "symbol+offset" style. which shows given pointer in "symbol+offset" style.
On the other hand, symbol-string type ('symstr') converts the given address to
"symbol+offset/symbolsize" style and stores it as a null-terminated string.
With 'symstr' type, you can filter the event with wildcard pattern of the
symbols, and you don't need to solve symbol name by yourself.
For $comm, the default type is "string"; any other type is invalid. For $comm, the default type is "string"; any other type is invalid.
.. _user_mem_access: .. _user_mem_access:
......
...@@ -2213,13 +2213,9 @@ int register_kretprobe(struct kretprobe *rp) ...@@ -2213,13 +2213,9 @@ int register_kretprobe(struct kretprobe *rp)
rp->kp.post_handler = NULL; rp->kp.post_handler = NULL;
/* Pre-allocate memory for max kretprobe instances */ /* Pre-allocate memory for max kretprobe instances */
if (rp->maxactive <= 0) { if (rp->maxactive <= 0)
#ifdef CONFIG_PREEMPTION
rp->maxactive = max_t(unsigned int, 10, 2*num_possible_cpus()); rp->maxactive = max_t(unsigned int, 10, 2*num_possible_cpus());
#else
rp->maxactive = num_possible_cpus();
#endif
}
#ifdef CONFIG_KRETPROBE_ON_RETHOOK #ifdef CONFIG_KRETPROBE_ON_RETHOOK
rp->rh = rethook_alloc((void *)rp, kretprobe_rethook_handler); rp->rh = rethook_alloc((void *)rp, kretprobe_rethook_handler);
if (!rp->rh) if (!rp->rh)
...@@ -2364,6 +2360,14 @@ static void kill_kprobe(struct kprobe *p) ...@@ -2364,6 +2360,14 @@ static void kill_kprobe(struct kprobe *p)
lockdep_assert_held(&kprobe_mutex); lockdep_assert_held(&kprobe_mutex);
/*
* The module is going away. We should disarm the kprobe which
* is using ftrace, because ftrace framework is still available at
* 'MODULE_STATE_GOING' notification.
*/
if (kprobe_ftrace(p) && !kprobe_disabled(p) && !kprobes_all_disarmed)
disarm_kprobe_ftrace(p);
p->flags |= KPROBE_FLAG_GONE; p->flags |= KPROBE_FLAG_GONE;
if (kprobe_aggrprobe(p)) { if (kprobe_aggrprobe(p)) {
/* /*
...@@ -2380,14 +2384,6 @@ static void kill_kprobe(struct kprobe *p) ...@@ -2380,14 +2384,6 @@ static void kill_kprobe(struct kprobe *p)
* the original probed function (which will be freed soon) any more. * the original probed function (which will be freed soon) any more.
*/ */
arch_remove_kprobe(p); arch_remove_kprobe(p);
/*
* The module is going away. We should disarm the kprobe which
* is using ftrace, because ftrace framework is still available at
* 'MODULE_STATE_GOING' notification.
*/
if (kprobe_ftrace(p) && !kprobe_disabled(p) && !kprobes_all_disarmed)
disarm_kprobe_ftrace(p);
} }
/* Disable one kprobe */ /* Disable one kprobe */
......
...@@ -5617,7 +5617,7 @@ static const char readme_msg[] = ...@@ -5617,7 +5617,7 @@ static const char readme_msg[] =
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n" "\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n" "\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
"\t <type>\\[<array-size>\\]\n" "\t symstr, <type>\\[<array-size>\\]\n"
#ifdef CONFIG_HIST_TRIGGERS #ifdef CONFIG_HIST_TRIGGERS
"\t field: <stype> <name>;\n" "\t field: <stype> <name>;\n"
"\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n" "\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n"
......
...@@ -76,9 +76,11 @@ const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; ...@@ -76,9 +76,11 @@ const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
/* Fetch type information table */ /* Fetch type information table */
static const struct fetch_type probe_fetch_types[] = { static const struct fetch_type probe_fetch_types[] = {
/* Special types */ /* Special types */
__ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, 1,
"__data_loc char[]"), "__data_loc char[]"),
__ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, __ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, 1,
"__data_loc char[]"),
__ASSIGN_FETCH_TYPE("symstr", string, string, sizeof(u32), 1, 1,
"__data_loc char[]"), "__data_loc char[]"),
/* Basic types */ /* Basic types */
ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u8, u8, 0),
...@@ -98,10 +100,15 @@ static const struct fetch_type probe_fetch_types[] = { ...@@ -98,10 +100,15 @@ static const struct fetch_type probe_fetch_types[] = {
ASSIGN_FETCH_TYPE_END ASSIGN_FETCH_TYPE_END
}; };
static const struct fetch_type *find_fetch_type(const char *type) static const struct fetch_type *find_fetch_type(const char *type, unsigned long flags)
{ {
int i; int i;
/* Reject the symbol/symstr for uprobes */
if (type && (flags & TPARG_FL_USER) &&
(!strcmp(type, "symbol") || !strcmp(type, "symstr")))
return NULL;
if (!type) if (!type)
type = DEFAULT_FETCH_TYPE_STR; type = DEFAULT_FETCH_TYPE_STR;
...@@ -119,13 +126,13 @@ static const struct fetch_type *find_fetch_type(const char *type) ...@@ -119,13 +126,13 @@ static const struct fetch_type *find_fetch_type(const char *type)
switch (bs) { switch (bs) {
case 8: case 8:
return find_fetch_type("u8"); return find_fetch_type("u8", flags);
case 16: case 16:
return find_fetch_type("u16"); return find_fetch_type("u16", flags);
case 32: case 32:
return find_fetch_type("u32"); return find_fetch_type("u32", flags);
case 64: case 64:
return find_fetch_type("u64"); return find_fetch_type("u64", flags);
default: default:
goto fail; goto fail;
} }
...@@ -478,7 +485,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, ...@@ -478,7 +485,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
DEREF_OPEN_BRACE); DEREF_OPEN_BRACE);
return -EINVAL; return -EINVAL;
} else { } else {
const struct fetch_type *t2 = find_fetch_type(NULL); const struct fetch_type *t2 = find_fetch_type(NULL, flags);
*tmp = '\0'; *tmp = '\0';
ret = parse_probe_arg(arg, t2, &code, end, flags, offs); ret = parse_probe_arg(arg, t2, &code, end, flags, offs);
...@@ -630,9 +637,9 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, ...@@ -630,9 +637,9 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
/* The type of $comm must be "string", and not an array. */ /* The type of $comm must be "string", and not an array. */
if (parg->count || (t && strcmp(t, "string"))) if (parg->count || (t && strcmp(t, "string")))
goto out; goto out;
parg->type = find_fetch_type("string"); parg->type = find_fetch_type("string", flags);
} else } else
parg->type = find_fetch_type(t); parg->type = find_fetch_type(t, flags);
if (!parg->type) { if (!parg->type) {
trace_probe_log_err(offset + (t ? (t - arg) : 0), BAD_TYPE); trace_probe_log_err(offset + (t ? (t - arg) : 0), BAD_TYPE);
goto out; goto out;
...@@ -662,16 +669,26 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, ...@@ -662,16 +669,26 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
ret = -EINVAL; ret = -EINVAL;
/* Store operation */ /* Store operation */
if (!strcmp(parg->type->name, "string") || if (parg->type->is_string) {
!strcmp(parg->type->name, "ustring")) { if (!strcmp(parg->type->name, "symstr")) {
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF && if (code->op != FETCH_OP_REG && code->op != FETCH_OP_STACK &&
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM && code->op != FETCH_OP_RETVAL && code->op != FETCH_OP_ARG &&
code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) { code->op != FETCH_OP_DEREF && code->op != FETCH_OP_TP_ARG) {
trace_probe_log_err(offset + (t ? (t - arg) : 0), trace_probe_log_err(offset + (t ? (t - arg) : 0),
BAD_STRING); BAD_SYMSTRING);
goto fail; goto fail;
}
} else {
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) {
trace_probe_log_err(offset + (t ? (t - arg) : 0),
BAD_STRING);
goto fail;
}
} }
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM || if (!strcmp(parg->type->name, "symstr") ||
(code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
code->op == FETCH_OP_DATA) || code->op == FETCH_OP_TP_ARG || code->op == FETCH_OP_DATA) || code->op == FETCH_OP_TP_ARG ||
parg->count) { parg->count) {
/* /*
...@@ -679,6 +696,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, ...@@ -679,6 +696,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
* must be kept, and if parg->count != 0, this is an * must be kept, and if parg->count != 0, this is an
* array of string pointers instead of string address * array of string pointers instead of string address
* itself. * itself.
* For the symstr, it doesn't need to dereference, thus
* it just get the value.
*/ */
code++; code++;
if (code->op != FETCH_OP_NOP) { if (code->op != FETCH_OP_NOP) {
...@@ -690,6 +709,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, ...@@ -690,6 +709,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
if (!strcmp(parg->type->name, "ustring") || if (!strcmp(parg->type->name, "ustring") ||
code->op == FETCH_OP_UDEREF) code->op == FETCH_OP_UDEREF)
code->op = FETCH_OP_ST_USTRING; code->op = FETCH_OP_ST_USTRING;
else if (!strcmp(parg->type->name, "symstr"))
code->op = FETCH_OP_ST_SYMSTR;
else else
code->op = FETCH_OP_ST_STRING; code->op = FETCH_OP_ST_STRING;
code->size = parg->type->size; code->size = parg->type->size;
...@@ -919,8 +940,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, ...@@ -919,8 +940,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
for (i = 0; i < tp->nr_args; i++) { for (i = 0; i < tp->nr_args; i++) {
parg = tp->args + i; parg = tp->args + i;
if (parg->count) { if (parg->count) {
if ((strcmp(parg->type->name, "string") == 0) || if (parg->type->is_string)
(strcmp(parg->type->name, "ustring") == 0))
fmt = ", __get_str(%s[%d])"; fmt = ", __get_str(%s[%d])";
else else
fmt = ", REC->%s[%d]"; fmt = ", REC->%s[%d]";
...@@ -928,8 +948,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, ...@@ -928,8 +948,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
pos += snprintf(buf + pos, LEN_OR_ZERO, pos += snprintf(buf + pos, LEN_OR_ZERO,
fmt, parg->name, j); fmt, parg->name, j);
} else { } else {
if ((strcmp(parg->type->name, "string") == 0) || if (parg->type->is_string)
(strcmp(parg->type->name, "ustring") == 0))
fmt = ", __get_str(%s)"; fmt = ", __get_str(%s)";
else else
fmt = ", REC->%s"; fmt = ", REC->%s";
......
...@@ -98,6 +98,7 @@ enum fetch_op { ...@@ -98,6 +98,7 @@ enum fetch_op {
FETCH_OP_ST_UMEM, /* Mem: .offset, .size */ FETCH_OP_ST_UMEM, /* Mem: .offset, .size */
FETCH_OP_ST_STRING, /* String: .offset, .size */ FETCH_OP_ST_STRING, /* String: .offset, .size */
FETCH_OP_ST_USTRING, /* User String: .offset, .size */ FETCH_OP_ST_USTRING, /* User String: .offset, .size */
FETCH_OP_ST_SYMSTR, /* Kernel Symbol String: .offset, .size */
// Stage 4 (modify) op // Stage 4 (modify) op
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
// Stage 5 (loop) op // Stage 5 (loop) op
...@@ -133,7 +134,8 @@ struct fetch_insn { ...@@ -133,7 +134,8 @@ struct fetch_insn {
struct fetch_type { struct fetch_type {
const char *name; /* Name of type */ const char *name; /* Name of type */
size_t size; /* Byte size of type */ size_t size; /* Byte size of type */
int is_signed; /* Signed flag */ bool is_signed; /* Signed flag */
bool is_string; /* String flag */
print_type_func_t print; /* Print functions */ print_type_func_t print; /* Print functions */
const char *fmt; /* Format string */ const char *fmt; /* Format string */
const char *fmttype; /* Name in format file */ const char *fmttype; /* Name in format file */
...@@ -177,16 +179,19 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol); ...@@ -177,16 +179,19 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
#define _ADDR_FETCH_TYPE(t) __ADDR_FETCH_TYPE(t) #define _ADDR_FETCH_TYPE(t) __ADDR_FETCH_TYPE(t)
#define ADDR_FETCH_TYPE _ADDR_FETCH_TYPE(BITS_PER_LONG) #define ADDR_FETCH_TYPE _ADDR_FETCH_TYPE(BITS_PER_LONG)
#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ #define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, str, _fmttype) \
{.name = _name, \ {.name = _name, \
.size = _size, \ .size = _size, \
.is_signed = sign, \ .is_signed = (bool)sign, \
.is_string = (bool)str, \
.print = PRINT_TYPE_FUNC_NAME(ptype), \ .print = PRINT_TYPE_FUNC_NAME(ptype), \
.fmt = PRINT_TYPE_FMT_NAME(ptype), \ .fmt = PRINT_TYPE_FMT_NAME(ptype), \
.fmttype = _fmttype, \ .fmttype = _fmttype, \
} }
/* Non string types can use these macros */
#define _ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ #define _ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \
__ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, #_fmttype) __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, 0, #_fmttype)
#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ #define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, ptype) _ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, ptype)
...@@ -353,7 +358,8 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char ...@@ -353,7 +358,8 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char
#define TPARG_FL_KERNEL BIT(1) #define TPARG_FL_KERNEL BIT(1)
#define TPARG_FL_FENTRY BIT(2) #define TPARG_FL_FENTRY BIT(2)
#define TPARG_FL_TPOINT BIT(3) #define TPARG_FL_TPOINT BIT(3)
#define TPARG_FL_MASK GENMASK(3, 0) #define TPARG_FL_USER BIT(4)
#define TPARG_FL_MASK GENMASK(4, 0)
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
const char *argv, unsigned int flags); const char *argv, unsigned int flags);
...@@ -431,6 +437,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, ...@@ -431,6 +437,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(ARRAY_TOO_BIG, "Array number is too big"), \ C(ARRAY_TOO_BIG, "Array number is too big"), \
C(BAD_TYPE, "Unknown type is specified"), \ C(BAD_TYPE, "Unknown type is specified"), \
C(BAD_STRING, "String accepts only memory argument"), \ C(BAD_STRING, "String accepts only memory argument"), \
C(BAD_SYMSTRING, "Symbol String doesn't accept data/userdata"), \
C(BAD_BITFIELD, "Invalid bitfield"), \ C(BAD_BITFIELD, "Invalid bitfield"), \
C(ARG_NAME_TOO_LONG, "Argument name is too long"), \ C(ARG_NAME_TOO_LONG, "Argument name is too long"), \
C(NO_ARG_NAME, "Argument name is not specified"), \ C(NO_ARG_NAME, "Argument name is not specified"), \
......
...@@ -67,6 +67,37 @@ probe_mem_read(void *dest, void *src, size_t size); ...@@ -67,6 +67,37 @@ probe_mem_read(void *dest, void *src, size_t size);
static nokprobe_inline int static nokprobe_inline int
probe_mem_read_user(void *dest, void *src, size_t size); probe_mem_read_user(void *dest, void *src, size_t size);
static nokprobe_inline int
fetch_store_symstrlen(unsigned long addr)
{
char namebuf[KSYM_SYMBOL_LEN];
int ret;
ret = sprint_symbol(namebuf, addr);
if (ret < 0)
return 0;
return ret + 1;
}
/*
* Fetch a null-terminated symbol string + offset. Caller MUST set *(u32 *)buf
* with max length and relative data location.
*/
static nokprobe_inline int
fetch_store_symstring(unsigned long addr, void *dest, void *base)
{
int maxlen = get_loc_len(*(u32 *)dest);
void *__dest;
if (unlikely(!maxlen))
return -ENOMEM;
__dest = get_loc_data(dest, base);
return sprint_symbol(__dest, addr);
}
/* From the 2nd stage, routine is same */ /* From the 2nd stage, routine is same */
static nokprobe_inline int static nokprobe_inline int
process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
...@@ -99,16 +130,22 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -99,16 +130,22 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
stage3: stage3:
/* 3rd stage: store value to buffer */ /* 3rd stage: store value to buffer */
if (unlikely(!dest)) { if (unlikely(!dest)) {
if (code->op == FETCH_OP_ST_STRING) { switch (code->op) {
case FETCH_OP_ST_STRING:
ret = fetch_store_strlen(val + code->offset); ret = fetch_store_strlen(val + code->offset);
code++; code++;
goto array; goto array;
} else if (code->op == FETCH_OP_ST_USTRING) { case FETCH_OP_ST_USTRING:
ret += fetch_store_strlen_user(val + code->offset); ret += fetch_store_strlen_user(val + code->offset);
code++; code++;
goto array; goto array;
} else case FETCH_OP_ST_SYMSTR:
ret += fetch_store_symstrlen(val + code->offset);
code++;
goto array;
default:
return -EILSEQ; return -EILSEQ;
}
} }
switch (code->op) { switch (code->op) {
...@@ -129,6 +166,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -129,6 +166,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
loc = *(u32 *)dest; loc = *(u32 *)dest;
ret = fetch_store_string_user(val + code->offset, dest, base); ret = fetch_store_string_user(val + code->offset, dest, base);
break; break;
case FETCH_OP_ST_SYMSTR:
loc = *(u32 *)dest;
ret = fetch_store_symstring(val + code->offset, dest, base);
break;
default: default:
return -EILSEQ; return -EILSEQ;
} }
......
...@@ -691,7 +691,8 @@ static int __trace_uprobe_create(int argc, const char **argv) ...@@ -691,7 +691,8 @@ static int __trace_uprobe_create(int argc, const char **argv)
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
trace_probe_log_set_index(i + 2); trace_probe_log_set_index(i + 2);
ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i], ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i],
is_return ? TPARG_FL_RETURN : 0); (is_return ? TPARG_FL_RETURN : 0) |
TPARG_FL_USER);
if (ret) if (ret)
goto error; goto error;
} }
......
...@@ -23,4 +23,9 @@ check_error 'p /bin/sh:10^%hoge' # BAD_ADDR_SUFFIX ...@@ -23,4 +23,9 @@ check_error 'p /bin/sh:10^%hoge' # BAD_ADDR_SUFFIX
check_error 'p /bin/sh:10(10)^%return' # BAD_REFCNT_SUFFIX check_error 'p /bin/sh:10(10)^%return' # BAD_REFCNT_SUFFIX
fi fi
# symstr is not supported by uprobe
if grep -q ".*symstr.*" README; then
check_error 'p /bin/sh:10 $stack0:^symstr' # BAD_TYPE
fi
exit 0 exit 0
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