From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794558511; b=FgtaM90S80wvhQVD19Fts+3DkJDXvRfTR/Fv7Q1nH9lFGJBIhsUikzl9ypOkzlDdQ5M/ uwSPrBzIB8xOQh93kUPE92BjLETmMACpJMsordu+85weUzR+wLSlZ2S21jzV0BAyJsjQx YITC6Y5zQNOKYihx/HLXxwz+eGxMP/SBsXPS6E6CNMvBjmvvJNxApXTGOlioo+XaNhYvY U96x2UsnGEJUqHxJ2fj0ZVKQ670/KdajDb8CH59L4Q39/DKFvzWAnuqYAhQ8XYpq7wqiJ A/QJYgKIc5CPRJ5CW1ELjUG4/1wy7lFJBDg/MJkf7MMsEBcZ5WbnqEfHbGbPOuwNq5A== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794558511; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:MIME-Version:Content-Type: Content-Transfer-Encoding; bh=C8iGJPQkgOp9klFTmgBec62xqq4e7YsKB+b3fh67mGg=; b=1+BVpyBFJ5PqxX962XdkV25SGYIF17WkVHQEK4QDgtwDnDEuqYJlpR62iuKXYUy15IIg CdDBzHmTt2FSe31NK1qaqIFgDxnuRourph5xgd6G7ivsb7485l3KcgureiCH5m9moThv9 TV+jDobfeY6OpMaXG8ab0ZTcm3MgvLoy28KCwQ7hZjMIdU9l8H0BPBZ72sk3DkXS6Q7ga CpYciEPExWar+3Jv/3bFHagDQy1NJhcgfW61Hsj1OGArs7uc8UPhZJGpC+O+JGnc/v4ur 3Ave3zKQjTSMfN4licMvWbRvlHZ+PqAi/OiOZFdFfHUPHEMwOG1o7kzGwFVqpHnpLfQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b="Yob/7N7+"; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5FE8C606BDF3; Fri, 13 Nov 2026 08:28:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=Yob/7N7+ Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8B6F21F000C6; Fri, 13 Nov 2026 08:28:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1794558511; bh=C8iGJPQkgOp9klFTmgBec62xqq4e7YsKB+b3fh67mGg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Yob/7N7+bnSfiD9Q+B7uON9rTFKOz/NjsYanZbu9oTGbw15xpMdBtei6ZtpiWtqut gz6BLResuQ2D6x3PcyI7c2ktnSDyITn3fiSHKNSMTkYZF0Yf2lRwI8lNpD8zkQTFjF IkG5uw0Jt75ia2J9VpIQp9uG3MOujZlxEn5Qsl+PdwCYfNetmcIC5hjB30K0LJCw7x KEQnSNZFj2EopwrTyl55qVhCf7rzStboV/bOnbVqdAx7+jmlymRC579xsbF1jqydZ1 hJA+F0kZnL13rbNI2oeE1Qk4cCqey0RcTOg9v/tzijrnCXiy7hLMSWaWKw1sl2W6gB 9e8xvA3X3avSA== From: "Masami Hiramatsu (Google)" To: Steven Rostedt , Masami Hiramatsu Cc: Mathieu Desnoyers , 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 Message-ID: <179455850711.403392.10512730891220678403.stgit@devnote2> X-Mailer: git-send-email 2.43.0 In-Reply-To: <179455847934.403392.15146485751358774349.stgit@devnote2> References: <179455847934.403392.15146485751358774349.stgit@devnote2> User-Agent: StGit/0.19 X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit From: Masami Hiramatsu (Google) 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) --- 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; }