xref: /linux/tools/testing/selftests/bpf/progs/sock_ops_get_sk.c (revision c4dde411bc366f568dbe33366253bbfea049e8ea)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include "vmlinux.h"
4 #include <bpf/bpf_helpers.h>
5 #include "bpf_misc.h"
6 
7 /*
8  * Test the SOCK_OPS_GET_SK() and SOCK_OPS_GET_FIELD() macros in
9  * sock_ops_convert_ctx_access() when dst_reg == src_reg.
10  *
11  * When dst_reg == src_reg, the macros borrow a temporary register to load
12  * is_fullsock / is_locked_tcp_sock, because dst_reg holds the ctx pointer
13  * and cannot be clobbered before ctx->sk / ctx->field is read. If
14  * is_fullsock == 0 (e.g., TCP_NEW_SYN_RECV with a request_sock), the macro
15  * must still zero dst_reg so the verifier's PTR_TO_SOCKET_OR_NULL /
16  * SCALAR_VALUE type is correct at runtime. A missing clear leaves a stale
17  * ctx pointer in dst_reg that passes NULL checks (GET_SK) or leaks a kernel
18  * address as a scalar (GET_FIELD).
19  *
20  * When dst_reg != src_reg, dst_reg itself is used to load is_fullsock, so
21  * the JEQ (dst_reg == 0) naturally leaves it zeroed on the !fullsock path.
22  */
23 
24 int bug_detected;
25 int null_seen;
26 
27 SEC("sockops")
28 __naked void sock_ops_get_sk_same_reg(void)
29 {
30 	asm volatile (
31 		"r7 = *(u32 *)(r1 + %[is_fullsock_off]);"
32 		"r1 = *(u64 *)(r1 + %[sk_off]);"
33 		"if r7 != 0 goto 2f;"
34 		"if r1 == 0 goto 1f;"
35 		"r1 = %[bug_detected] ll;"
36 		"r2 = 1;"
37 		"*(u32 *)(r1 + 0) = r2;"
38 		"goto 2f;"
39 	"1:"
40 		"r1 = %[null_seen] ll;"
41 		"r2 = 1;"
42 		"*(u32 *)(r1 + 0) = r2;"
43 	"2:"
44 		"r0 = 1;"
45 		"exit;"
46 		:
47 		: __imm_const(is_fullsock_off, offsetof(struct bpf_sock_ops, is_fullsock)),
48 		  __imm_const(sk_off, offsetof(struct bpf_sock_ops, sk)),
49 		  __imm_addr(bug_detected),
50 		  __imm_addr(null_seen)
51 		: __clobber_all);
52 }
53 
54 /* SOCK_OPS_GET_FIELD: same-register, is_locked_tcp_sock == 0 path. */
55 int field_bug_detected;
56 int field_null_seen;
57 
58 SEC("sockops")
59 __naked void sock_ops_get_field_same_reg(void)
60 {
61 	asm volatile (
62 		"r7 = *(u32 *)(r1 + %[is_fullsock_off]);"
63 		"r1 = *(u32 *)(r1 + %[snd_cwnd_off]);"
64 		"if r7 != 0 goto 2f;"
65 		"if r1 == 0 goto 1f;"
66 		"r1 = %[field_bug_detected] ll;"
67 		"r2 = 1;"
68 		"*(u32 *)(r1 + 0) = r2;"
69 		"goto 2f;"
70 	"1:"
71 		"r1 = %[field_null_seen] ll;"
72 		"r2 = 1;"
73 		"*(u32 *)(r1 + 0) = r2;"
74 	"2:"
75 		"r0 = 1;"
76 		"exit;"
77 		:
78 		: __imm_const(is_fullsock_off, offsetof(struct bpf_sock_ops, is_fullsock)),
79 		  __imm_const(snd_cwnd_off, offsetof(struct bpf_sock_ops, snd_cwnd)),
80 		  __imm_addr(field_bug_detected),
81 		  __imm_addr(field_null_seen)
82 		: __clobber_all);
83 }
84 
85 /* SOCK_OPS_GET_SK: different-register, is_fullsock == 0 path. */
86 int diff_reg_bug_detected;
87 int diff_reg_null_seen;
88 
89 SEC("sockops")
90 __naked void sock_ops_get_sk_diff_reg(void)
91 {
92 	asm volatile (
93 		"r7 = r1;"
94 		"r6 = *(u32 *)(r7 + %[is_fullsock_off]);"
95 		"r2 = *(u64 *)(r7 + %[sk_off]);"
96 		"if r6 != 0 goto 2f;"
97 		"if r2 == 0 goto 1f;"
98 		"r1 = %[diff_reg_bug_detected] ll;"
99 		"r3 = 1;"
100 		"*(u32 *)(r1 + 0) = r3;"
101 		"goto 2f;"
102 	"1:"
103 		"r1 = %[diff_reg_null_seen] ll;"
104 		"r3 = 1;"
105 		"*(u32 *)(r1 + 0) = r3;"
106 	"2:"
107 		"r0 = 1;"
108 		"exit;"
109 		:
110 		: __imm_const(is_fullsock_off, offsetof(struct bpf_sock_ops, is_fullsock)),
111 		  __imm_const(sk_off, offsetof(struct bpf_sock_ops, sk)),
112 		  __imm_addr(diff_reg_bug_detected),
113 		  __imm_addr(diff_reg_null_seen)
114 		: __clobber_all);
115 }
116 
117 char _license[] SEC("license") = "GPL";
118