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