public inbox for linux-kselftest@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf v3 0/2] bpf: fix sock_ops rtt_min OOB read
@ 2026-04-17  2:31 Werner Kasselman
  2026-04-17  2:31 ` [PATCH 1/2] bpf: extract SOCK_OPS_LOAD_TCP_SOCK_FIELD from SOCK_OPS_GET_FIELD Werner Kasselman
  2026-04-17  2:31 ` [PATCH 2/2] bpf: guard sock_ops rtt_min against non-locked tcp_sock Werner Kasselman
  0 siblings, 2 replies; 3+ messages in thread
From: Werner Kasselman @ 2026-04-17  2:31 UTC (permalink / raw)
  To: bpf@vger.kernel.org, netdev@vger.kernel.org
  Cc: andrii@kernel.org, ast@kernel.org, brakmo@fb.com,
	daniel@iogearbox.net, davem@davemloft.net, eddyz87@gmail.com,
	edumazet@google.com, haoluo@google.com, horms@kernel.org,
	john.fastabend@gmail.com, jolsa@kernel.org, kpsingh@kernel.org,
	kuba@kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org, martin.lau@linux.dev,
	pabeni@redhat.com, sdf@fomichev.me, shuah@kernel.org,
	song@kernel.org, yonghong.song@linux.dev, jiayuan.chen@linux.dev,
	Werner Kasselman

Patch 1 from v2 is upstream as 10f86a2a5c91 ("bpf: Fix same-register
dst/src OOB read and pointer leak in sock_ops"), so this reroll keeps
only the remaining work for ctx->rtt_min.

Patch 1 extracts the guarded field-load sequence out of
SOCK_OPS_GET_FIELD() so the rtt_min sub-field access can reuse the same
is_locked_tcp_sock guard and dst_reg == src_reg handling without
open-coding it again.

Patch 2 uses that helper for rtt_min and extends the landed
sock_ops_get_sk selftest with an rtt_min subtest covering the
request_sock-backed !fullsock path.

Changes since v2:
- drop the overlapping dst_reg == src_reg fix, now upstream as
  10f86a2a5c91
- keep only the helper extraction plus the rtt_min fix
- add an rtt_min subtest on top of the landed sock_ops_get_sk selftest

Werner Kasselman (2):
  bpf: extract SOCK_OPS_LOAD_TCP_SOCK_FIELD from SOCK_OPS_GET_FIELD
  bpf: guard sock_ops rtt_min against non-locked tcp_sock

 net/core/filter.c                             | 31 ++++++++++---------
 .../bpf/prog_tests/sock_ops_get_sk.c          |  9 ++++++
 .../selftests/bpf/progs/sock_ops_get_sk.c     | 31 +++++++++++++++++++
 3 files changed, 57 insertions(+), 14 deletions(-)

-- 
2.43.0

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] bpf: extract SOCK_OPS_LOAD_TCP_SOCK_FIELD from SOCK_OPS_GET_FIELD
  2026-04-17  2:31 [PATCH bpf v3 0/2] bpf: fix sock_ops rtt_min OOB read Werner Kasselman
@ 2026-04-17  2:31 ` Werner Kasselman
  2026-04-17  2:31 ` [PATCH 2/2] bpf: guard sock_ops rtt_min against non-locked tcp_sock Werner Kasselman
  1 sibling, 0 replies; 3+ messages in thread
From: Werner Kasselman @ 2026-04-17  2:31 UTC (permalink / raw)
  To: bpf@vger.kernel.org, netdev@vger.kernel.org
  Cc: andrii@kernel.org, ast@kernel.org, brakmo@fb.com,
	daniel@iogearbox.net, davem@davemloft.net, eddyz87@gmail.com,
	edumazet@google.com, haoluo@google.com, horms@kernel.org,
	john.fastabend@gmail.com, jolsa@kernel.org, kpsingh@kernel.org,
	kuba@kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org, martin.lau@linux.dev,
	pabeni@redhat.com, sdf@fomichev.me, shuah@kernel.org,
	song@kernel.org, yonghong.song@linux.dev, jiayuan.chen@linux.dev,
	Werner Kasselman

Extract the guarded sock_ops field load sequence out of
SOCK_OPS_GET_FIELD() into SOCK_OPS_LOAD_TCP_SOCK_FIELD(FIELD_SIZE,
FIELD_OFFSET) so callers that need a custom field offset can reuse the
same is_locked_tcp_sock guard and dst_reg == src_reg handling.

This is needed for rtt_min, which reads a minmax sub-field rather than a
direct struct member.

No functional change.

Signed-off-by: Werner Kasselman <werner@verivus.com>
---
 net/core/filter.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/net/core/filter.c b/net/core/filter.c
index fcfcb72663ca..e8ad062f63bc 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -10535,12 +10535,10 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
 	struct bpf_insn *insn = insn_buf;
 	int off;
 
-/* Helper macro for adding read access to tcp_sock or sock fields. */
-#define SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ)			      \
+/* Helper macro for adding guarded read access to tcp_sock fields. */
+#define SOCK_OPS_LOAD_TCP_SOCK_FIELD(FIELD_SIZE, FIELD_OFFSET)		      \
 	do {								      \
 		int fullsock_reg = si->dst_reg, reg = BPF_REG_9, jmp = 2;     \
-		BUILD_BUG_ON(sizeof_field(OBJ, OBJ_FIELD) >		      \
-			     sizeof_field(struct bpf_sock_ops, BPF_FIELD));   \
 		if (si->dst_reg == reg || si->src_reg == reg)		      \
 			reg--;						      \
 		if (si->dst_reg == reg || si->src_reg == reg)		      \
@@ -10567,10 +10565,9 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
 						struct bpf_sock_ops_kern, sk),\
 				      si->dst_reg, si->src_reg,		      \
 				      offsetof(struct bpf_sock_ops_kern, sk));\
-		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(OBJ,		      \
-						       OBJ_FIELD),	      \
+		*insn++ = BPF_LDX_MEM(FIELD_SIZE,			      \
 				      si->dst_reg, si->dst_reg,		      \
-				      offsetof(OBJ, OBJ_FIELD));	      \
+				      FIELD_OFFSET);			      \
 		if (si->dst_reg == si->src_reg)	{			      \
 			*insn++ = BPF_JMP_A(2);				      \
 			*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg,	      \
@@ -10580,6 +10577,14 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
 		}							      \
 	} while (0)
 
+#define SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ)			      \
+	do {								      \
+		BUILD_BUG_ON(sizeof_field(OBJ, OBJ_FIELD) >		      \
+			     sizeof_field(struct bpf_sock_ops, BPF_FIELD));   \
+		SOCK_OPS_LOAD_TCP_SOCK_FIELD(BPF_FIELD_SIZEOF(OBJ, OBJ_FIELD),\
+					     offsetof(OBJ, OBJ_FIELD));       \
+	} while (0)
+
 #define SOCK_OPS_GET_SK()							      \
 	do {								      \
 		int fullsock_reg = si->dst_reg, reg = BPF_REG_9, jmp = 1;     \
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] bpf: guard sock_ops rtt_min against non-locked tcp_sock
  2026-04-17  2:31 [PATCH bpf v3 0/2] bpf: fix sock_ops rtt_min OOB read Werner Kasselman
  2026-04-17  2:31 ` [PATCH 1/2] bpf: extract SOCK_OPS_LOAD_TCP_SOCK_FIELD from SOCK_OPS_GET_FIELD Werner Kasselman
@ 2026-04-17  2:31 ` Werner Kasselman
  1 sibling, 0 replies; 3+ messages in thread
From: Werner Kasselman @ 2026-04-17  2:31 UTC (permalink / raw)
  To: bpf@vger.kernel.org, netdev@vger.kernel.org
  Cc: andrii@kernel.org, ast@kernel.org, brakmo@fb.com,
	daniel@iogearbox.net, davem@davemloft.net, eddyz87@gmail.com,
	edumazet@google.com, haoluo@google.com, horms@kernel.org,
	john.fastabend@gmail.com, jolsa@kernel.org, kpsingh@kernel.org,
	kuba@kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org, martin.lau@linux.dev,
	pabeni@redhat.com, sdf@fomichev.me, shuah@kernel.org,
	song@kernel.org, yonghong.song@linux.dev, jiayuan.chen@linux.dev,
	Werner Kasselman, stable@vger.kernel.org

sock_ops_convert_ctx_access() reads rtt_min without the
is_locked_tcp_sock guard used for every other tcp_sock field. On
request_sock-backed sock_ops callbacks, sk points at a
tcp_request_sock and the converted load reads past the end of the
allocation.

Reuse SOCK_OPS_LOAD_TCP_SOCK_FIELD() for the rtt_min access and compute
the offset with offsetof(struct minmax_sample, v). This leaves the byte
addressed unchanged from the old sizeof_field(struct minmax_sample, t)
expression, while making rtt_min consistent with every other tcp_sock
field.

This also picks up the same dst_reg == src_reg handling used by the
other guarded field loads. Extend the sock_ops_get_sk selftest with an
rtt_min subtest that checks request_sock-backed !fullsock callbacks read
zero instead of leaking request_sock-adjacent memory.

Found via AST-based call-graph analysis using sqry.

Fixes: 44f0e43037d3 ("bpf: Add support for reading sk_state and more")
Cc: stable@vger.kernel.org
Signed-off-by: Werner Kasselman <werner@verivus.com>
---
 net/core/filter.c                             | 12 +++----
 .../bpf/prog_tests/sock_ops_get_sk.c          |  9 ++++++
 .../selftests/bpf/progs/sock_ops_get_sk.c     | 31 +++++++++++++++++++
 3 files changed, 45 insertions(+), 7 deletions(-)

diff --git a/net/core/filter.c b/net/core/filter.c
index e8ad062f63bc..9c43193a5c39 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -10827,14 +10827,12 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
 			     sizeof(struct minmax));
 		BUILD_BUG_ON(sizeof(struct minmax) <
 			     sizeof(struct minmax_sample));
+		BUILD_BUG_ON(offsetof(struct tcp_sock, rtt_min) +
+			     offsetof(struct minmax_sample, v) > S16_MAX);
 
-		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(
-						struct bpf_sock_ops_kern, sk),
-				      si->dst_reg, si->src_reg,
-				      offsetof(struct bpf_sock_ops_kern, sk));
-		*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg,
-				      offsetof(struct tcp_sock, rtt_min) +
-				      sizeof_field(struct minmax_sample, t));
+		off = offsetof(struct tcp_sock, rtt_min) +
+		      offsetof(struct minmax_sample, v);
+		SOCK_OPS_LOAD_TCP_SOCK_FIELD(BPF_W, off);
 		break;
 
 	case offsetof(struct bpf_sock_ops, bpf_sock_ops_cb_flags):
diff --git a/tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c b/tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c
index 343d92c4df30..1aea4c97d5d3 100644
--- a/tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c
+++ b/tools/testing/selftests/bpf/prog_tests/sock_ops_get_sk.c
@@ -70,6 +70,15 @@ void test_ns_sock_ops_get_sk(void)
 		ASSERT_EQ(skel->bss->diff_reg_bug_detected, 0, "diff_reg_bug_not_detected");
 	}
 
+	/* Test sock_ops rtt_min access in !fullsock callbacks */
+	if (test__start_subtest("get_rtt_min")) {
+		run_sock_ops_test(cgroup_fd,
+				  bpf_program__fd(skel->progs.sock_ops_get_rtt_min));
+		ASSERT_EQ(skel->bss->rtt_min_null_seen, 1, "rtt_min_null_seen");
+		ASSERT_EQ(skel->bss->rtt_min_bug_detected, 0,
+			  "rtt_min_bug_not_detected");
+	}
+
 	sock_ops_get_sk__destroy(skel);
 close_cgroup:
 	close(cgroup_fd);
diff --git a/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c b/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c
index 3a0689f8ce7c..dee07da8901e 100644
--- a/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c
+++ b/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c
@@ -114,4 +114,35 @@ __naked void sock_ops_get_sk_diff_reg(void)
 		: __clobber_all);
 }
 
+/* sock_ops rtt_min access: different-register, is_locked_tcp_sock == 0 path (TCP_NEW_SYN_RECV). */
+int rtt_min_bug_detected;
+int rtt_min_null_seen;
+
+SEC("sockops")
+__naked void sock_ops_get_rtt_min(void)
+{
+	asm volatile (
+		"r7 = *(u32 *)(r1 + %[is_fullsock_off]);"
+		"r2 = *(u32 *)(r1 + %[rtt_min_off]);"
+		"if r7 != 0 goto 2f;"
+		"if r2 == 0 goto 1f;"
+		"r1 = %[rtt_min_bug_detected] ll;"
+		"r3 = 1;"
+		"*(u32 *)(r1 + 0) = r3;"
+		"goto 2f;"
+	"1:"
+		"r1 = %[rtt_min_null_seen] ll;"
+		"r3 = 1;"
+		"*(u32 *)(r1 + 0) = r3;"
+	"2:"
+		"r0 = 1;"
+		"exit;"
+		:
+		: __imm_const(is_fullsock_off, offsetof(struct bpf_sock_ops, is_fullsock)),
+		  __imm_const(rtt_min_off, offsetof(struct bpf_sock_ops, rtt_min)),
+		  __imm_addr(rtt_min_bug_detected),
+		  __imm_addr(rtt_min_null_seen)
+		: __clobber_all);
+}
+
 char _license[] SEC("license") = "GPL";
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-04-17  2:31 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17  2:31 [PATCH bpf v3 0/2] bpf: fix sock_ops rtt_min OOB read Werner Kasselman
2026-04-17  2:31 ` [PATCH 1/2] bpf: extract SOCK_OPS_LOAD_TCP_SOCK_FIELD from SOCK_OPS_GET_FIELD Werner Kasselman
2026-04-17  2:31 ` [PATCH 2/2] bpf: guard sock_ops rtt_min against non-locked tcp_sock Werner Kasselman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox