From: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>,
Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org
Subject: [PATCH v3 03/13] tracing/probes: Fix BTF bitfield parsing and out-of-bounds buffer write
Date: Fri, 13 Nov 2026 17:28:27 +0900 [thread overview]
Message-ID: <179455850711.403392.10512730891220678403.stgit@devnote2> (raw)
In-Reply-To: <179455847934.403392.15146485751358774349.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
BTF bitfield parsing in trace_probe.c assumes a 64-bit (8-byte) container
when computing FETCH_OP_MOD_BF shift amounts and basesize, whereas the
preceding store instruction only allocates and writes the field's fetch
type size (which may be 1, 2, or 4 bytes when kflag=1). At runtime,
fetch_apply_bitfield() performs an 8-byte read-modify-write on the
smaller ring buffer slot, corrupting adjacent ring buffer data or writing
past the end of the event record.
In addition, signed bitfields with bit widths other than 8/16/32/64 fail
to resolve a fetch type, bitfields whose width is a multiple of 8 inside
a larger integer or spanning a byte boundary fail to emit FETCH_OP_MOD_BF
or select a container wide enough for bitoffs + bitsize, kflag=0 bitfield
offsets >= 8 bits are not folded into the byte offset, and big-endian
architectures use the little-endian left-shift formula in
parse_btf_bitfield().
Compute the bitfield byte and bit offsets consistently during field
lookup, select the smallest unsigned fetch type that covers the bit
offset and bit width, and calculate the FETCH_OP_MOD_BF parameters from
the selected fetch type size for both big- and little-endian bitfields.
Fixes: c440adfbe302 ("tracing/probes: Support BTF based data structure field access")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v3:
- Use ctx->last_bitoffs as code->lshift on __BIG_ENDIAN_BITFIELD
architectures in parse_btf_bitfield().
- Fold BTF_INT_OFFSET(intdata) into bitoffs before checking alignment
and check !is_power_of_2(type->size) in get_bitoffset_of_field().
Changes in v2:
- Check (bitoffs % 8) in get_bitoffset_of_field() so unaligned
byte-sized kflag=0 bitfields also record ctx->last_bitsize.
---
kernel/trace/trace_probe.c | 61 +++++++++++++++++++++++++++++++++-----------
1 file changed, 46 insertions(+), 15 deletions(-)
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index a880a90642d7..54cd3c2584a8 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -486,6 +486,18 @@ static const char *fetch_type_from_btf_type(struct btf *btf,
{
u32 intdata;
+ if (ctx->last_bitsize) {
+ u32 bits = ctx->last_bitsize + ctx->last_bitoffs;
+
+ if (bits <= 8)
+ return "u8";
+ if (bits <= 16)
+ return "u16";
+ if (bits <= 32)
+ return "u32";
+ return "u64";
+ }
+
/* TODO: const char * could be converted as a string */
switch (BTF_INFO_KIND(type->info)) {
case BTF_KIND_ENUM:
@@ -520,10 +532,6 @@ static const char *fetch_type_from_btf_type(struct btf *btf,
case 64:
return "u64";
}
- /* bitfield, size is encoded in the type */
- ctx->last_bitsize = BTF_INT_BITS(intdata);
- ctx->last_bitoffs += BTF_INT_OFFSET(intdata);
- return "u64";
}
}
/* TODO: support other types */
@@ -653,6 +661,12 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
/* Add anonymous structure/union offset */
bitoffs += anon_offs;
+ type = btf_type_skip_modifiers(btf, field->type, NULL);
+ if (!type) {
+ trace_probe_log_err(ctx->offset, BAD_BTF_TID);
+ return -EINVAL;
+ }
+
/* Accumulate the bit-offsets of the dot-connected fields */
if (btf_type_kflag(container_type)) {
bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
@@ -660,12 +674,15 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
} else {
bitoffs += field->offset;
ctx->last_bitsize = 0;
- }
-
- type = btf_type_skip_modifiers(btf, field->type, NULL);
- if (!type) {
- trace_probe_log_err(ctx->offset, BAD_BTF_TID);
- return -EINVAL;
+ if (BTF_INFO_KIND(type->info) == BTF_KIND_INT) {
+ u32 intdata = btf_type_int(type);
+
+ bitoffs += BTF_INT_OFFSET(intdata);
+ if ((bitoffs % 8) ||
+ BTF_INT_BITS(intdata) != BYTES_TO_BITS(type->size) ||
+ !is_power_of_2(type->size))
+ ctx->last_bitsize = BTF_INT_BITS(intdata);
+ }
}
if (next)
@@ -858,13 +875,22 @@ static const struct fetch_type *find_fetch_type_from_btf_type(
}
static int parse_btf_bitfield(struct fetch_insn **pcode,
+ const struct fetch_type *t,
struct traceprobe_parse_context *ctx)
{
struct fetch_insn *code = *pcode;
- if ((ctx->last_bitsize % 8 == 0) && ctx->last_bitoffs == 0)
+ if ((ctx->last_bitsize == 0 ||
+ ctx->last_bitsize == BYTES_TO_BITS(t->size)) &&
+ ctx->last_bitoffs == 0)
return 0;
+ if (ctx->last_bitsize == 0 ||
+ BYTES_TO_BITS(t->size) < (ctx->last_bitsize + ctx->last_bitoffs)) {
+ trace_probe_log_err(ctx->offset, BAD_BITFIELD);
+ return -EINVAL;
+ }
+
code++;
if (code->op != FETCH_OP_NOP) {
trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
@@ -873,9 +899,13 @@ static int parse_btf_bitfield(struct fetch_insn **pcode,
*pcode = code;
code->op = FETCH_OP_MOD_BF;
- code->lshift = 64 - (ctx->last_bitsize + ctx->last_bitoffs);
- code->rshift = 64 - ctx->last_bitsize;
- code->basesize = 64 / 8;
+#ifdef __BIG_ENDIAN_BITFIELD
+ code->lshift = ctx->last_bitoffs;
+#else
+ code->lshift = BYTES_TO_BITS(t->size) - (ctx->last_bitsize + ctx->last_bitoffs);
+#endif
+ code->rshift = BYTES_TO_BITS(t->size) - ctx->last_bitsize;
+ code->basesize = t->size;
return 0;
}
@@ -1085,6 +1115,7 @@ static int parse_btf_arg(char *varname,
}
static int parse_btf_bitfield(struct fetch_insn **pcode,
+ const struct fetch_type *t,
struct traceprobe_parse_context *ctx)
{
trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
@@ -1995,7 +2026,7 @@ static int finalize_fetch_insn(struct fetch_insn *code,
} else if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) &&
ctx->last_type) {
/* If user not specified the type, try parsing BTF bitfield. */
- ret = parse_btf_bitfield(&code, ctx);
+ ret = parse_btf_bitfield(&code, parg->type, ctx);
if (ret)
return ret;
}
next prev parent reply other threads:[~2026-11-13 8:28 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 8:27 [PATCH v3 00/13] tracing/probes: Fix various bugs in dynamic probe events Masami Hiramatsu (Google)
2026-11-13 8:28 ` [PATCH v3 01/13] tracing/probes: Check enabled and busy state before unlinking sibling probes Masami Hiramatsu (Google)
2026-11-13 8:28 ` [PATCH v3 02/13] tracing/probes: Enable newly appended probe when target event is enabled Masami Hiramatsu (Google)
2026-11-13 8:28 ` Masami Hiramatsu (Google) [this message]
2026-11-13 8:28 ` [PATCH v3 04/13] tracing/probes: Fix missing BTF kind checks in char pointer/array helpers Masami Hiramatsu (Google)
2026-11-13 8:28 ` [PATCH v3 05/13] tracing/eprobe: Fix 1-byte signed field fetch in get_event_field() Masami Hiramatsu (Google)
2026-11-13 8:28 ` [PATCH v3 06/13] tracing/eprobe: Disallow attaching eprobes to fprobe events Masami Hiramatsu (Google)
2026-11-13 8:29 ` [PATCH v3 07/13] tracing/probes: Fix user-space dereference string array store type Masami Hiramatsu (Google)
2026-11-13 8:29 ` [PATCH v3 08/13] tracing/probes: Reject array count on non-dereference string sources Masami Hiramatsu (Google)
2026-11-13 8:29 ` [PATCH v3 09/13] tracing/uprobes: Fix memory leak of tail in __trace_uprobe_create() Masami Hiramatsu (Google)
2026-11-13 8:29 ` [PATCH v3 10/13] tracing/probes: Allow symstr type for function entry arguments in return probes Masami Hiramatsu (Google)
2026-11-13 8:29 ` [PATCH v3 11/13] tracing/probes: Reject empty fetch expression for %pd and %pD Masami Hiramatsu (Google)
2026-11-13 8:29 ` [PATCH v3 12/13] tracing/probes: Validate command prefix in kprobe, uprobe, and eprobe Masami Hiramatsu (Google)
2026-11-13 8:29 ` [PATCH v3 13/13] tracing/probes: Propagate error codes in eprobe and fprobe creation Masami Hiramatsu (Google)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179455850711.403392.10512730891220678403.stgit@devnote2 \
--to=mhiramat@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=rostedt@goodmis.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®