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