* [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
@ 2026-05-19 17:01 Steven Rostedt
2026-05-19 17:28 ` Steven Rostedt
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Steven Rostedt @ 2026-05-19 17:01 UTC (permalink / raw)
To: LKML, Linux trace kernel, bpf
Cc: Masami Hiramatsu, Mathieu Desnoyers, Mark Rutland, Peter Zijlstra,
Namhyung Kim, Takaya Saeki, Douglas Raillard, Tom Zanussi,
Andrew Morton, Thomas Gleixner, Ian Rogers, Jiri Olsa
From: Steven Rostedt <rostedt@goodmis.org>
Add syntax to the parsing of eprobes to be able to typecast a trace event
field that is a pointer to a structure.
Currently, a dereference must be a number, where the user has to figure
out manually the offset of a member of a structure that they want to
dereference.
But for event probes that records a field that happens to be a pointer to
a structure, it cannot dereference these values with BTF naming, but
must use numerical offsets.
For example, to find out what device a sk_buff is pointing to in the
net_dev_xmit trace event, one must first use gdb to find the offsets of the
members of the structures:
(gdb) p &((struct sk_buff *)0)->dev
$1 = (struct net_device **) 0x10
(gdb) p &((struct net_device *)0)->name
$2 = (char (*)[16]) 0x118
And then use the raw numbers to dereference:
# echo 'e:xmit net.net_dev_xmit +0x118(+0x10($skbaddr)):string' >> dynamic_events
If BTF is in the kernel, then instead, the skbaddr can be typecast to
sk_buff and use the normal dereference logic.
# echo 'e:xmit net.net_dev_xmit (sk_buff)skbaddr->dev->name:string' >> dynamic_events
# echo 1 > events/eprobes/xmit/enable
# cat trace
[..]
sshd-session-1022 [000] b..2. 860.249343: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.250061: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.250142: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.263553: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.283820: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.302716: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.322905: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.342828: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.362268: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.382335: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.400856: xmit: (net.net_dev_xmit) arg1="enp7s0"
sshd-session-1022 [000] b..2. 860.419893: xmit: (net.net_dev_xmit) arg1="enp7s0"
The syntax is simply: (STRUCT)(FIELD)->MEMBER[->MEMBER..]
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Changes from v4: https://patch.msgid.link/20260518232312.0c78f055@gandalf.local.home
- Only describe this for eprobes, as that is currently the only user of
this. Kprobes registers can come later. (Sashiko)
- Update the documentation to only be in the eprobetrace.rst.
- Make the btf value separate for structures and functions. As a function
may use a BTF from a module, it can point to a structure that is in
vmlinux. Make them separate and add a ctx_btf() helper function to
figure out which one to use. (Masami Hiramatsu)
- Remove updating query_btf_context() by having it check funcname first.
The above ctx_btf() helper fixes this.
- Rename TPARG_FL_STRUCT to TPARG_FL_TYPECAST (Masami Hiramatsu)
- Have the typecast code call parse_btf_arg() directly, instead of going
through parse_probe_vars(). This fixes issues with updating the pcode
value, and simplifies the formatting and code (Sashiko).
Added a parse_trace_event() helper to duplicate the handling of
TPARG_FL_TEVENT.
- Use "goto found_type;" instead of relying on type being NULL in
parse_btf_arg(). (Masami Hiramatsu)
- Add query_btf_struct() stub function when CONFIG_PROBE_EVENTS_BTF_ARGS
is not defined. (kernel test robot)
- Remove need for '*' in typecast (Masami Hiramatsu)
- Have the event fields not use '$' when the typecast is used.
(Masami Hiramatsu)
Documentation/trace/eprobetrace.rst | 4 +
kernel/trace/trace_probe.c | 158 ++++++++++++++++++++++------
kernel/trace/trace_probe.h | 4 +
3 files changed, 135 insertions(+), 31 deletions(-)
diff --git a/Documentation/trace/eprobetrace.rst b/Documentation/trace/eprobetrace.rst
index 89b5157cfab8..fe3602540569 100644
--- a/Documentation/trace/eprobetrace.rst
+++ b/Documentation/trace/eprobetrace.rst
@@ -46,6 +46,10 @@ Synopsis of eprobe_events
(x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
"string", "ustring", "symbol", "symstr" and "bitfield" are
supported.
+ (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
+ a pointer to STRUCT and then derference the pointer defined by
+ ->MEMBER. Note that when this is used, the FIELD name does not
+ need to be prefixed with a '$'.
Types
-----
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index e0d3a0da26af..cd54deb985f4 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -376,11 +376,17 @@ static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type)
&& BTF_INT_BITS(intdata) == 8;
}
+static struct btf *ctx_btf(struct traceprobe_parse_context *ctx)
+{
+ return ctx->flags & TPARG_FL_TYPECAST ?
+ ctx->struct_btf : ctx->btf;
+}
+
static int check_prepare_btf_string_fetch(char *typename,
struct fetch_insn **pcode,
struct traceprobe_parse_context *ctx)
{
- struct btf *btf = ctx->btf;
+ struct btf *btf = ctx_btf(ctx);
if (!btf || !ctx->last_type)
return 0;
@@ -464,6 +470,27 @@ static const char *fetch_type_from_btf_type(struct btf *btf,
return NULL;
}
+static int query_btf_struct(const char *sname, struct traceprobe_parse_context *ctx)
+{
+ int id;
+
+ if (!ctx->struct_btf) {
+ struct btf *btf;
+
+ id = bpf_find_btf_id(sname, BTF_KIND_STRUCT, &btf);
+ if (id < 0)
+ return id;
+ ctx->struct_btf = btf;
+ } else {
+ id = btf_find_by_name_kind(ctx->struct_btf, sname, BTF_KIND_STRUCT);
+ if (id < 0)
+ return id;
+ }
+
+ ctx->last_struct = btf_type_by_id(ctx->struct_btf, id);
+ return 0;
+}
+
static int query_btf_context(struct traceprobe_parse_context *ctx)
{
const struct btf_param *param;
@@ -515,6 +542,10 @@ static void clear_btf_context(struct traceprobe_parse_context *ctx)
ctx->params = NULL;
ctx->nr_params = 0;
}
+ if (ctx->struct_btf) {
+ btf_put(ctx->struct_btf);
+ ctx->last_struct = NULL;
+ }
}
/* Return 1 if the field separator is arrow operator ('->') */
@@ -554,22 +585,29 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
struct fetch_insn *code = *pcode;
const struct btf_member *field;
u32 bitoffs, anon_offs;
+ bool is_struct = ctx->flags & TPARG_FL_TYPECAST;
+ struct btf *btf = ctx_btf(ctx);
char *next;
int is_ptr;
s32 tid;
do {
- /* Outer loop for solving arrow operator ('->') */
- if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
- trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
- return -EINVAL;
- }
- /* Convert a struct pointer type to a struct type */
- type = btf_type_skip_modifiers(ctx->btf, type->type, &tid);
- if (!type) {
- trace_probe_log_err(ctx->offset, BAD_BTF_TID);
- return -EINVAL;
+ if (!is_struct) {
+ /* Outer loop for solving arrow operator ('->') */
+ if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
+ trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
+ return -EINVAL;
+ }
+
+ /* Convert a struct pointer type to a struct type */
+ type = btf_type_skip_modifiers(btf, type->type, &tid);
+ if (!type) {
+ trace_probe_log_err(ctx->offset, BAD_BTF_TID);
+ return -EINVAL;
+ }
}
+ /* Only the first type can skip being a pointer */
+ is_struct = false;
bitoffs = 0;
do {
@@ -580,7 +618,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
return is_ptr;
anon_offs = 0;
- field = btf_find_struct_member(ctx->btf, type, fieldname,
+ field = btf_find_struct_member(btf, type, fieldname,
&anon_offs);
if (IS_ERR(field)) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
@@ -602,7 +640,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
ctx->last_bitsize = 0;
}
- type = btf_type_skip_modifiers(ctx->btf, field->type, &tid);
+ type = btf_type_skip_modifiers(btf, field->type, &tid);
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return -EINVAL;
@@ -627,6 +665,26 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
return 0;
}
+
+static int parse_trace_event(char *arg, struct fetch_insn *code,
+ struct traceprobe_parse_context *ctx)
+{
+ int ret;
+
+ if (code->data)
+ return -EFAULT;
+ ret = parse_trace_event_arg(arg, code, ctx);
+ if (!ret)
+ return 0;
+ if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) {
+ code->op = FETCH_OP_COMM;
+ return 0;
+ }
+ /* backward compatibility */
+ ctx->offset = 0;
+ return -EINVAL;
+}
+
static int __store_entry_arg(struct trace_probe *tp, int argnum);
static int parse_btf_arg(char *varname,
@@ -636,11 +694,12 @@ static int parse_btf_arg(char *varname,
struct fetch_insn *code = *pcode;
const struct btf_param *params;
const struct btf_type *type;
+ struct btf *btf = ctx_btf(ctx);
char *field = NULL;
int i, is_ptr, ret;
u32 tid;
- if (WARN_ON_ONCE(!ctx->funcname))
+ if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TYPECAST)))
return -EINVAL;
is_ptr = split_next_field(varname, &field, ctx);
@@ -653,6 +712,14 @@ static int parse_btf_arg(char *varname,
return -EOPNOTSUPP;
}
+ if (ctx->flags & TPARG_FL_TEVENT) {
+ int ret;
+
+ ret = parse_trace_event(varname, code, ctx);
+ if (ret < 0)
+ return ret;
+ }
+
if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
code->op = FETCH_OP_RETVAL;
/* Check whether the function return type is not void */
@@ -672,7 +739,7 @@ static int parse_btf_arg(char *varname,
return 0;
}
- if (!ctx->btf) {
+ if (!btf) {
ret = query_btf_context(ctx);
if (ret < 0 || ctx->nr_params == 0) {
trace_probe_log_err(ctx->offset, NO_BTF_ENTRY);
@@ -682,7 +749,7 @@ static int parse_btf_arg(char *varname,
params = ctx->params;
for (i = 0; i < ctx->nr_params; i++) {
- const char *name = btf_name_by_offset(ctx->btf, params[i].name_off);
+ const char *name = btf_name_by_offset(btf, params[i].name_off);
if (name && !strcmp(name, varname)) {
if (tparg_is_function_entry(ctx->flags)) {
@@ -704,15 +771,22 @@ static int parse_btf_arg(char *varname,
goto found;
}
}
+
+ if (ctx->flags & TPARG_FL_TYPECAST) {
+ type = ctx->last_struct;
+ goto found_type;
+ }
+
trace_probe_log_err(ctx->offset, NO_BTFARG);
return -ENOENT;
found:
- type = btf_type_skip_modifiers(ctx->btf, tid, &tid);
+ type = btf_type_skip_modifiers(btf, tid, &tid);
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return -EINVAL;
}
+found_type:
/* Initialize the last type information */
ctx->last_type = type;
ctx->last_bitoffs = 0;
@@ -727,7 +801,7 @@ static int parse_btf_arg(char *varname,
static const struct fetch_type *find_fetch_type_from_btf_type(
struct traceprobe_parse_context *ctx)
{
- struct btf *btf = ctx->btf;
+ struct btf *btf = ctx_btf(ctx);
const char *typestr = NULL;
if (btf && ctx->last_type)
@@ -764,6 +838,11 @@ static void clear_btf_context(struct traceprobe_parse_context *ctx)
ctx->btf = NULL;
}
+static int query_btf_struct(const char *sname, struct traceprobe_parse_context *ctx)
+{
+ return -EOPNOTSUPP;
+}
+
static int query_btf_context(struct traceprobe_parse_context *ctx)
{
return -EOPNOTSUPP;
@@ -953,18 +1032,9 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
int len;
if (ctx->flags & TPARG_FL_TEVENT) {
- if (code->data)
- return -EFAULT;
- ret = parse_trace_event_arg(arg, code, ctx);
- if (!ret)
- return 0;
- if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) {
- code->op = FETCH_OP_COMM;
- return 0;
- }
- /* backward compatibility */
- ctx->offset = 0;
- goto inval;
+ if (parse_trace_event(arg, code, ctx) < 0)
+ goto inval;
+ return 0;
}
if (str_has_prefix(arg, "retval")) {
@@ -1231,6 +1301,30 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
code->op = FETCH_OP_IMM;
}
break;
+ case '(':
+ tmp = strchr(arg, ')');
+ if (!tmp) {
+ trace_probe_log_err(ctx->offset + strlen(arg),
+ DEREF_OPEN_BRACE);
+ return -EINVAL;
+ }
+ *tmp = '\0';
+ ret = query_btf_struct(arg + 1, ctx);
+ *tmp = ')';
+
+ if (ret < 0) {
+ trace_probe_log_err(ctx->offset + 1, NO_PTR_STRCT);
+ return -EINVAL;
+ }
+
+ ctx->flags |= TPARG_FL_TYPECAST;
+ tmp++;
+
+ ctx->offset += tmp - arg;
+ ret = parse_btf_arg(tmp, pcode, end, ctx);
+ ctx->flags &= ~TPARG_FL_TYPECAST;
+ ctx->last_struct = NULL;
+ break;
default:
if (isalpha(arg[0]) || arg[0] == '_') { /* BTF variable */
if (!tparg_is_function_entry(ctx->flags) &&
@@ -1504,6 +1598,7 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
code[FETCH_INSN_MAX - 1].op = FETCH_OP_END;
ctx->last_type = NULL;
+ ctx->last_struct = NULL;
ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1],
ctx);
if (ret < 0)
@@ -1705,13 +1800,14 @@ static int sprint_nth_btf_arg(int idx, const char *type,
struct traceprobe_parse_context *ctx)
{
const char *name;
+ struct btf *btf = ctx_btf(ctx);
int ret;
if (idx >= ctx->nr_params) {
trace_probe_log_err(0, NO_BTFARG);
return -ENOENT;
}
- name = btf_name_by_offset(ctx->btf, ctx->params[idx].name_off);
+ name = btf_name_by_offset(btf, ctx->params[idx].name_off);
if (!name) {
trace_probe_log_err(0, NO_BTF_ENTRY);
return -ENOENT;
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 262d8707a3df..6735b8b2432b 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -394,6 +394,7 @@ static inline int traceprobe_get_entry_data_size(struct trace_probe *tp)
* TPARG_FL_KERNEL and TPARG_FL_USER are also mutually exclusive.
* TPARG_FL_FPROBE and TPARG_FL_TPOINT are optional but it should be with
* TPARG_FL_KERNEL.
+ * TPARG_FL_TYPECAST is set if an argument was typecast to a structure.
*/
#define TPARG_FL_RETURN BIT(0)
#define TPARG_FL_KERNEL BIT(1)
@@ -402,6 +403,7 @@ static inline int traceprobe_get_entry_data_size(struct trace_probe *tp)
#define TPARG_FL_USER BIT(4)
#define TPARG_FL_FPROBE BIT(5)
#define TPARG_FL_TPOINT BIT(6)
+#define TPARG_FL_TYPECAST BIT(7)
#define TPARG_FL_LOC_MASK GENMASK(4, 0)
static inline bool tparg_is_function_entry(unsigned int flags)
@@ -422,7 +424,9 @@ struct traceprobe_parse_context {
const struct btf_param *params; /* Parameter of the function */
s32 nr_params; /* The number of the parameters */
struct btf *btf; /* The BTF to be used */
+ struct btf *struct_btf; /* The BTF to be used for structs */
const struct btf_type *last_type; /* Saved type */
+ const struct btf_type *last_struct; /* Saved structure */
u32 last_bitoffs; /* Saved bitoffs */
u32 last_bitsize; /* Saved bitsize */
struct trace_probe *tp;
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
2026-05-19 17:01 [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers Steven Rostedt
@ 2026-05-19 17:28 ` Steven Rostedt
2026-05-19 17:37 ` Steven Rostedt
[not found] ` <20260519174848.176A6C2BCB3@smtp.kernel.org>
` (2 subsequent siblings)
3 siblings, 1 reply; 6+ messages in thread
From: Steven Rostedt @ 2026-05-19 17:28 UTC (permalink / raw)
To: LKML, Linux trace kernel, bpf
Cc: Masami Hiramatsu, Mathieu Desnoyers, Mark Rutland, Peter Zijlstra,
Namhyung Kim, Takaya Saeki, Douglas Raillard, Tom Zanussi,
Andrew Morton, Thomas Gleixner, Ian Rogers, Jiri Olsa
On Tue, 19 May 2026 13:01:44 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:
> @@ -636,11 +694,12 @@ static int parse_btf_arg(char *varname,
> struct fetch_insn *code = *pcode;
> const struct btf_param *params;
> const struct btf_type *type;
> + struct btf *btf = ctx_btf(ctx);
> char *field = NULL;
> int i, is_ptr, ret;
> u32 tid;
>
> - if (WARN_ON_ONCE(!ctx->funcname))
> + if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TYPECAST)))
> return -EINVAL;
>
> is_ptr = split_next_field(varname, &field, ctx);
> @@ -653,6 +712,14 @@ static int parse_btf_arg(char *varname,
> return -EOPNOTSUPP;
> }
>
> + if (ctx->flags & TPARG_FL_TEVENT) {
> + int ret;
> +
> + ret = parse_trace_event(varname, code, ctx);
> + if (ret < 0)
> + return ret;
> + }
> +
> if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
> code->op = FETCH_OP_RETVAL;
> /* Check whether the function return type is not void */
> @@ -672,7 +739,7 @@ static int parse_btf_arg(char *varname,
> return 0;
> }
>
> - if (!ctx->btf) {
> + if (!btf) {
> ret = query_btf_context(ctx);
Oops, need a:
btf = ctx->btf;
here!
-- Steve
> if (ret < 0 || ctx->nr_params == 0) {
> trace_probe_log_err(ctx->offset, NO_BTF_ENTRY);
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
2026-05-19 17:28 ` Steven Rostedt
@ 2026-05-19 17:37 ` Steven Rostedt
0 siblings, 0 replies; 6+ messages in thread
From: Steven Rostedt @ 2026-05-19 17:37 UTC (permalink / raw)
To: LKML, Linux trace kernel, bpf
Cc: Masami Hiramatsu, Mathieu Desnoyers, Mark Rutland, Peter Zijlstra,
Namhyung Kim, Takaya Saeki, Douglas Raillard, Tom Zanussi,
Andrew Morton, Thomas Gleixner, Ian Rogers, Jiri Olsa
On Tue, 19 May 2026 13:28:30 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:
> On Tue, 19 May 2026 13:01:44 -0400
> Steven Rostedt <rostedt@goodmis.org> wrote:
>
> > @@ -636,11 +694,12 @@ static int parse_btf_arg(char *varname,
> > struct fetch_insn *code = *pcode;
> > const struct btf_param *params;
> > const struct btf_type *type;
> > + struct btf *btf = ctx_btf(ctx);
> > char *field = NULL;
> > int i, is_ptr, ret;
> > u32 tid;
> >
> > - if (WARN_ON_ONCE(!ctx->funcname))
> > + if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TYPECAST)))
> > return -EINVAL;
> >
> > is_ptr = split_next_field(varname, &field, ctx);
> > @@ -653,6 +712,14 @@ static int parse_btf_arg(char *varname,
> > return -EOPNOTSUPP;
> > }
> >
> > + if (ctx->flags & TPARG_FL_TEVENT) {
> > + int ret;
> > +
> > + ret = parse_trace_event(varname, code, ctx);
> > + if (ret < 0)
> > + return ret;
Actually, since typecasting is currently only for eprobes, I'm going to
shuffle the code around a bit to only have the TYPECAST affect TEVENT,
and do the jump to found_type from here.
-- Steve
> > + }
> > +
> > if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
> > code->op = FETCH_OP_RETVAL;
> > /* Check whether the function return type is not void */
> > @@ -672,7 +739,7 @@ static int parse_btf_arg(char *varname,
> > return 0;
> > }
> >
> > - if (!ctx->btf) {
> > + if (!btf) {
> > ret = query_btf_context(ctx);
>
> Oops, need a:
>
> btf = ctx->btf;
>
> here!
>
> -- Steve
>
> > if (ret < 0 || ctx->nr_params == 0) {
> > trace_probe_log_err(ctx->offset, NO_BTF_ENTRY);
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
[not found] ` <20260519174848.176A6C2BCB3@smtp.kernel.org>
@ 2026-05-19 18:17 ` Steven Rostedt
0 siblings, 0 replies; 6+ messages in thread
From: Steven Rostedt @ 2026-05-19 18:17 UTC (permalink / raw)
To: sashiko-bot
Cc: sashiko-reviews, bpf, LKML, Linux trace kernel, Masami Hiramatsu
On Tue, 19 May 2026 17:48:47 +0000
sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
> - [High] Using `(STRUCT)` typecasts for non-eprobes leaves the base fetch instruction uninitialized (`FETCH_OP_NOP`), silently breaking the probe.
> - [High] Prematurely clearing `TPARG_FL_TYPECAST` causes cross-BTF type confusion and breaks string fetches.
> - [Medium] Erroneous caching of `struct_btf` restricts a single trace probe to using typecasts from only one BTF object, preventing cross-module lookups.
> - [Medium] Cross-BTF string table access in `parse_btf_arg()` causes invalid string comparisons.
> - [Low] `clear_btf_context()` fails to clear `ctx->struct_btf` after dropping its reference.
> --
>
> commit de4b6287a7069fde24b0e0f679a545a06fb9ae6c
> Author: Steven Rostedt <rostedt@goodmis.org>
>
> tracing/eprobes: Allow use of BTF names to dereference pointers
>
> Add syntax to the parsing of eprobes to be able to typecast a trace event
> field that is a pointer to a structure.
>
> > diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
> > index e1c73065dae51..2bc9f4ca8aa7e 100644
> > --- a/kernel/trace/trace_probe.c
> > +++ b/kernel/trace/trace_probe.c
> > @@ -464,6 +470,27 @@ static const char *fetch_type_from_btf_type(struct btf *btf,
> > return NULL;
> > }
> >
> > +static int query_btf_struct(const char *sname, struct traceprobe_parse_context *ctx)
> > +{
> > + int id;
> > +
> > + if (!ctx->struct_btf) {
> > + struct btf *btf;
> > +
> > + id = bpf_find_btf_id(sname, BTF_KIND_STRUCT, &btf);
> > + if (id < 0)
> > + return id;
> > + ctx->struct_btf = btf;
> > + } else {
> > + id = btf_find_by_name_kind(ctx->struct_btf, sname, BTF_KIND_STRUCT);
>
> [Severity: Medium]
> Does caching struct_btf here restrict a single trace probe event to using
> typecasts from only one BTF object?
>
> If a probe attempts to typecast structures from two different kernel
> modules, wouldn't the lookup for the second module's struct fail because
> it only searches the cached BTF object of the first module (or vmlinux)?
Hmm, good point. I guess I should clear the struct_btf at the end of
the '(' switch case.
>
> > + if (id < 0)
> > + return id;
> > + }
> > +
> > + ctx->last_struct = btf_type_by_id(ctx->struct_btf, id);
> > + return 0;
> > +}
>
> [ ... ]
>
> > @@ -515,6 +542,10 @@ static void clear_btf_context(struct traceprobe_parse_context *ctx)
> > ctx->params = NULL;
> > ctx->nr_params = 0;
> > }
> > + if (ctx->struct_btf) {
> > + btf_put(ctx->struct_btf);
> > + ctx->last_struct = NULL;
>
> [Severity: Low]
> Should ctx->struct_btf be explicitly set to NULL after btf_put() drops
> the reference?
I'm thinking of dropping it in the '(' switch case.
>
> While the traceprobe_parse_context might be destroyed shortly after
> parsing completes, leaving a dangling pointer could potentially lead to a
> use-after-free and double-free if any future refactoring reuses the
> parsing context across multiple trace events.
>
> > + }
> > }
>
> [ ... ]
>
> > @@ -672,7 +739,7 @@ static int parse_btf_arg(char *varname,
> > return 0;
> > }
> >
> > - if (!ctx->btf) {
> > + if (!btf) {
> > ret = query_btf_context(ctx);
> > if (ret < 0 || ctx->nr_params == 0) {
> > trace_probe_log_err(ctx->offset, NO_BTF_ENTRY);
> > @@ -682,7 +749,7 @@ static int parse_btf_arg(char *varname,
> > params = ctx->params;
> >
> > for (i = 0; i < ctx->nr_params; i++) {
> > - const char *name = btf_name_by_offset(ctx->btf, params[i].name_off);
> > + const char *name = btf_name_by_offset(btf, params[i].name_off);
>
> [Severity: Medium]
> When parsing a typecast argument, btf evaluates to ctx->struct_btf. The
> parameter matching loop then calls btf_name_by_offset() using that btf.
>
> Since params[i].name_off originates from the function's BTF (ctx->btf,
> populated by previous non-typecast arguments), could this lead to reading
> from the wrong string table if ctx->struct_btf is a different kernel
> module?
The new patch will skip this for TYPECAST arguments. Well, at least for
trace events.
>
> This might result in invalid string comparisons or returning
> (invalid-name-offset) if it reads out of bounds.
>
> >
> > if (name && !strcmp(name, varname)) {
> > if (tparg_is_function_entry(ctx->flags)) {
>
> [ ... ]
>
> > @@ -1231,6 +1301,30 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
> > code->op = FETCH_OP_IMM;
> > }
> > break;
> > + case '(':
> > + tmp = strchr(arg, ')');
> > + if (!tmp) {
> > + trace_probe_log_err(ctx->offset + strlen(arg),
> > + DEREF_OPEN_BRACE);
> > + return -EINVAL;
> > + }
> > + *tmp = '\0';
> > + ret = query_btf_struct(arg + 1, ctx);
> > + *tmp = ')';
> > +
> > + if (ret < 0) {
> > + trace_probe_log_err(ctx->offset + 1, NO_PTR_STRCT);
> > + return -EINVAL;
> > + }
> > +
> > + ctx->flags |= TPARG_FL_TYPECAST;
> > + tmp++;
> > +
> > + ctx->offset += tmp - arg;
> > + ret = parse_btf_arg(tmp, pcode, end, ctx);
>
> [Severity: High]
> When parse_probe_arg() evaluates the (STRUCT) syntax for kprobes or
> fprobes, it sets TPARG_FL_TYPECAST and calls parse_btf_arg(). Inside
> parse_btf_arg(), ctx_btf() evaluates to ctx->struct_btf, so it bypasses
> query_btf_context() and jumps to found_type.
>
> However, it doesn't seem to initialize code->op with a valid base fetch
> instruction like FETCH_OP_ARG or FETCH_OP_REG.
>
> Could this leave the base instruction as FETCH_OP_NOP (0)? If so, it
> seems the base address would be 0, causing subsequent field dereferences
> to safely fail on 0 + offset due to copy_from_kernel_nofault(). This might
> allow unsupported syntax like (sk_buff)%di->dev to parse but silently
> fetch nothing.
Yeah, I may have to force this not to work with function types.
>
> > + ctx->flags &= ~TPARG_FL_TYPECAST;
>
> [Severity: High]
> The TPARG_FL_TYPECAST flag is cleared here, but ctx->last_type still
> points to a type originating from ctx->struct_btf.
>
> Later in traceprobe_parse_probe_arg_body(),
> check_prepare_btf_string_fetch() is called. Since the flag is cleared,
> ctx_btf() will incorrectly return ctx->btf (vmlinux for kprobes, or
> NULL for eprobes).
>
> Does this cause check_prepare_btf_string_fetch() to return 0 for eprobes
> and skip adding the necessary dummy FETCH_OP_DEREF for string fetches?
> Without this dummy dereference, string fetches on character pointers
> in eprobes might fail by reading the raw pointer value itself as a string.
>
> Also, for kprobes, could this cause type confusion if
> btf_type_skip_modifiers() looks up a module's type ID inside the vmlinux
> BTF?
As mentioned above, I need to put and clear the last_struct here.
Thanks,
-- Steve
>
> > + ctx->last_struct = NULL;
> > + break;
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
2026-05-19 17:01 [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers Steven Rostedt
2026-05-19 17:28 ` Steven Rostedt
[not found] ` <20260519174848.176A6C2BCB3@smtp.kernel.org>
@ 2026-05-19 21:09 ` kernel test robot
2026-05-19 22:03 ` kernel test robot
3 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-05-19 21:09 UTC (permalink / raw)
To: Steven Rostedt, LKML, Linux trace kernel, bpf
Cc: oe-kbuild-all, Masami Hiramatsu, Mathieu Desnoyers, Mark Rutland,
Peter Zijlstra, Namhyung Kim, Takaya Saeki, Douglas Raillard,
Tom Zanussi, Andrew Morton, Linux Memory Management List,
Thomas Gleixner, Ian Rogers, Jiri Olsa
Hi Steven,
kernel test robot noticed the following build errors:
[auto build test ERROR on trace/for-next]
[also build test ERROR on linus/master v7.1-rc4 next-20260519]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Steven-Rostedt/tracing-eprobes-Allow-use-of-BTF-names-to-dereference-pointers/20260520-011353
base: https://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace for-next
patch link: https://lore.kernel.org/r/20260519130144.40e71a00%40fedora
patch subject: [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
config: s390-randconfig-r071-20260520 (https://download.01.org/0day-ci/archive/20260520/202605200427.0xXjKghz-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 8.5.0
smatch: v0.5.0-9185-gbcc58b9c
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260520/202605200427.0xXjKghz-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202605200427.0xXjKghz-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
kernel/trace/trace_probe.c: In function 'parse_probe_vars':
>> kernel/trace/trace_probe.c:1035:7: error: implicit declaration of function 'parse_trace_event'; did you mean 'parse_trace_event_arg'? [-Werror=implicit-function-declaration]
if (parse_trace_event(arg, code, ctx) < 0)
^~~~~~~~~~~~~~~~~
parse_trace_event_arg
kernel/trace/trace_probe.c: In function 'sprint_nth_btf_arg':
>> kernel/trace/trace_probe.c:1803:20: error: implicit declaration of function 'ctx_btf' [-Werror=implicit-function-declaration]
struct btf *btf = ctx_btf(ctx);
^~~~~~~
>> kernel/trace/trace_probe.c:1803:20: warning: initialization of 'struct btf *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
At top level:
>> kernel/trace/trace_probe.c:318:12: warning: 'parse_trace_event_arg' defined but not used [-Wunused-function]
static int parse_trace_event_arg(char *arg, struct fetch_insn *code,
^~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +1035 kernel/trace/trace_probe.c
1020
1021 /* Parse $vars. @orig_arg points '$', which syncs to @ctx->offset */
1022 static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
1023 struct fetch_insn **pcode,
1024 struct fetch_insn *end,
1025 struct traceprobe_parse_context *ctx)
1026 {
1027 struct fetch_insn *code = *pcode;
1028 int err = TP_ERR_BAD_VAR;
1029 char *arg = orig_arg + 1;
1030 unsigned long param;
1031 int ret = 0;
1032 int len;
1033
1034 if (ctx->flags & TPARG_FL_TEVENT) {
> 1035 if (parse_trace_event(arg, code, ctx) < 0)
1036 goto inval;
1037 return 0;
1038 }
1039
1040 if (str_has_prefix(arg, "retval")) {
1041 if (!(ctx->flags & TPARG_FL_RETURN)) {
1042 err = TP_ERR_RETVAL_ON_PROBE;
1043 goto inval;
1044 }
1045 if (!(ctx->flags & TPARG_FL_KERNEL) ||
1046 !IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS)) {
1047 code->op = FETCH_OP_RETVAL;
1048 return 0;
1049 }
1050 return parse_btf_arg(orig_arg, pcode, end, ctx);
1051 }
1052
1053 len = str_has_prefix(arg, "stack");
1054 if (len) {
1055
1056 if (arg[len] == '\0') {
1057 code->op = FETCH_OP_STACKP;
1058 return 0;
1059 }
1060
1061 if (isdigit(arg[len])) {
1062 ret = kstrtoul(arg + len, 10, ¶m);
1063 if (ret)
1064 goto inval;
1065
1066 if ((ctx->flags & TPARG_FL_KERNEL) &&
1067 param > PARAM_MAX_STACK) {
1068 err = TP_ERR_BAD_STACK_NUM;
1069 goto inval;
1070 }
1071 code->op = FETCH_OP_STACK;
1072 code->param = (unsigned int)param;
1073 return 0;
1074 }
1075 goto inval;
1076 }
1077
1078 if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) {
1079 code->op = FETCH_OP_COMM;
1080 return 0;
1081 }
1082
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
2026-05-19 17:01 [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers Steven Rostedt
` (2 preceding siblings ...)
2026-05-19 21:09 ` kernel test robot
@ 2026-05-19 22:03 ` kernel test robot
3 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-05-19 22:03 UTC (permalink / raw)
To: Steven Rostedt, LKML, Linux trace kernel, bpf
Cc: oe-kbuild-all, Masami Hiramatsu, Mathieu Desnoyers, Mark Rutland,
Peter Zijlstra, Namhyung Kim, Takaya Saeki, Douglas Raillard,
Tom Zanussi, Andrew Morton, Linux Memory Management List,
Thomas Gleixner, Ian Rogers, Jiri Olsa
Hi Steven,
kernel test robot noticed the following build errors:
[auto build test ERROR on trace/for-next]
[also build test ERROR on linus/master v7.1-rc4 next-20260519]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Steven-Rostedt/tracing-eprobes-Allow-use-of-BTF-names-to-dereference-pointers/20260520-011353
base: https://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace for-next
patch link: https://lore.kernel.org/r/20260519130144.40e71a00%40fedora
patch subject: [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers
config: arc-defconfig (https://download.01.org/0day-ci/archive/20260520/202605200549.zZkT9xWc-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260520/202605200549.zZkT9xWc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202605200549.zZkT9xWc-lkp@intel.com/
All errors (new ones prefixed by >>):
kernel/trace/trace_probe.c: In function 'parse_probe_vars':
kernel/trace/trace_probe.c:1035:21: error: implicit declaration of function 'parse_trace_event'; did you mean 'parse_trace_event_arg'? [-Wimplicit-function-declaration]
1035 | if (parse_trace_event(arg, code, ctx) < 0)
| ^~~~~~~~~~~~~~~~~
| parse_trace_event_arg
kernel/trace/trace_probe.c: In function 'sprint_nth_btf_arg':
kernel/trace/trace_probe.c:1803:27: error: implicit declaration of function 'ctx_btf' [-Wimplicit-function-declaration]
1803 | struct btf *btf = ctx_btf(ctx);
| ^~~~~~~
>> kernel/trace/trace_probe.c:1803:27: error: initialization of 'struct btf *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
kernel/trace/trace_probe.c: At top level:
kernel/trace/trace_probe.c:318:12: warning: 'parse_trace_event_arg' defined but not used [-Wunused-function]
318 | static int parse_trace_event_arg(char *arg, struct fetch_insn *code,
| ^~~~~~~~~~~~~~~~~~~~~
vim +1803 kernel/trace/trace_probe.c
1797
1798 static int sprint_nth_btf_arg(int idx, const char *type,
1799 char *buf, int bufsize,
1800 struct traceprobe_parse_context *ctx)
1801 {
1802 const char *name;
> 1803 struct btf *btf = ctx_btf(ctx);
1804 int ret;
1805
1806 if (idx >= ctx->nr_params) {
1807 trace_probe_log_err(0, NO_BTFARG);
1808 return -ENOENT;
1809 }
1810 name = btf_name_by_offset(btf, ctx->params[idx].name_off);
1811 if (!name) {
1812 trace_probe_log_err(0, NO_BTF_ENTRY);
1813 return -ENOENT;
1814 }
1815 ret = snprintf(buf, bufsize, "%s%s", name, type);
1816 if (ret >= bufsize) {
1817 trace_probe_log_err(0, ARGS_2LONG);
1818 return -E2BIG;
1819 }
1820 return ret;
1821 }
1822
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-05-19 22:04 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-19 17:01 [PATCH v5] tracing/eprobes: Allow use of BTF names to dereference pointers Steven Rostedt
2026-05-19 17:28 ` Steven Rostedt
2026-05-19 17:37 ` Steven Rostedt
[not found] ` <20260519174848.176A6C2BCB3@smtp.kernel.org>
2026-05-19 18:17 ` Steven Rostedt
2026-05-19 21:09 ` kernel test robot
2026-05-19 22:03 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox