1 /* 2 * Check decoding of socket filters. 3 * 4 * Copyright (c) 2017 Dmitry V. Levin <ldv (at) altlinux.org> 5 * Copyright (c) 2017-2018 The strace developers. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "tests.h" 32 33 #include <stdio.h> 34 #include <unistd.h> 35 #include <netinet/in.h> 36 #include <sys/socket.h> 37 #include <linux/filter.h> 38 39 /* SO_GET_FILTER was introduced by Linux commit v3.8-rc1~139^2~518 */ 40 #ifndef SO_GET_FILTER 41 # define SO_GET_FILTER SO_ATTACH_FILTER 42 #endif 43 44 #define HEX_FMT "%#x" 45 46 #if XLAT_RAW 47 # define XLAT_FMT HEX_FMT 48 # define XLAT_ARGS(a_) (a_) 49 #elif XLAT_VERBOSE 50 # define XLAT_FMT HEX_FMT " /* %s */" 51 # define XLAT_ARGS(a_) (a_), #a_ 52 #else 53 # define XLAT_FMT "%s" 54 # define XLAT_ARGS(a_) #a_ 55 #endif 56 57 #define PRINT_STMT(pfx, code_fmt, k_fmt, ...) \ 58 printf("%sBPF_STMT(" code_fmt ", " k_fmt ")", pfx, __VA_ARGS__) 59 60 #define PRINT_JUMP(pfx, code_fmt, k, jt, jf, ...) \ 61 printf("%sBPF_JUMP(" code_fmt ", %#x, %#x, %#x)", \ 62 pfx, __VA_ARGS__, k, jt, jf) 63 64 static const struct sock_filter bpf_filter[] = { 65 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_LL_OFF+4), 66 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_NET_OFF+8), 67 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF+SKF_AD_PROTOCOL), 68 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, IPPROTO_UDP, 0, 5), 69 BPF_STMT(BPF_LD|BPF_W|BPF_LEN, 0), 70 BPF_JUMP(BPF_JMP|BPF_K|BPF_JGE, 100, 0, 3), 71 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 42), 72 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 'a', 0, 1), 73 BPF_STMT(BPF_RET|BPF_K, -1U), 74 BPF_STMT(BPF_RET|BPF_K, 0) 75 }; 76 77 static void 78 print_filter(void) 79 { 80 PRINT_STMT("[", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 81 XLAT_FMT "+4", 82 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS), 83 XLAT_ARGS(SKF_LL_OFF)); 84 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 85 XLAT_FMT "+8", 86 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS), 87 XLAT_ARGS(SKF_NET_OFF)); 88 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 89 XLAT_FMT "+" XLAT_FMT, 90 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS), 91 XLAT_ARGS(SKF_AD_OFF), XLAT_ARGS(SKF_AD_PROTOCOL)); 92 PRINT_JUMP(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 93 IPPROTO_UDP, 0, 5, 94 XLAT_ARGS(BPF_JMP), XLAT_ARGS(BPF_K), XLAT_ARGS(BPF_JEQ)); 95 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 96 HEX_FMT, 97 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_W), XLAT_ARGS(BPF_LEN), 98 0); 99 PRINT_JUMP(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 100 100, 0, 3, 101 XLAT_ARGS(BPF_JMP), XLAT_ARGS(BPF_K), XLAT_ARGS(BPF_JGE)); 102 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 103 HEX_FMT, 104 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS), 105 42); 106 PRINT_JUMP(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT, 107 'a', 0, 1, 108 XLAT_ARGS(BPF_JMP), XLAT_ARGS(BPF_K), XLAT_ARGS(BPF_JEQ)); 109 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT, 110 HEX_FMT, 111 XLAT_ARGS(BPF_RET), XLAT_ARGS(BPF_K), 112 -1U); 113 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT, 114 HEX_FMT, 115 XLAT_ARGS(BPF_RET), XLAT_ARGS(BPF_K), 116 0); 117 putchar(']'); 118 } 119 120 static const char *errstr; 121 122 static int 123 get_filter(int fd, void *val, socklen_t *len) 124 { 125 int rc = getsockopt(fd, SOL_SOCKET, SO_GET_FILTER, val, len); 126 errstr = sprintrc(rc); 127 return rc; 128 } 129 130 static int 131 set_filter(int fd, void *val, socklen_t len) 132 { 133 int rc = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, val, len); 134 errstr = sprintrc(rc); 135 return rc; 136 } 137 138 int 139 main(void) 140 { 141 int rc; 142 struct sock_filter *const filter = 143 tail_memdup(bpf_filter, sizeof(bpf_filter)); 144 void *const efault = filter + ARRAY_SIZE(bpf_filter); 145 TAIL_ALLOC_OBJECT_CONST_PTR(struct sock_fprog, prog); 146 TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len); 147 148 prog->len = ARRAY_SIZE(bpf_filter); 149 prog->filter = filter; 150 151 int fd = socket(AF_INET, SOCK_DGRAM, 0); 152 if (fd < 0) 153 perror_msg_and_skip("socket AF_INET SOCK_DGRAM"); 154 155 /* query sock_filter program length -> 0 */ 156 *len = BPF_MAXINSNS; 157 rc = get_filter(fd, NULL, len); 158 if (rc) 159 perror_msg_and_skip("getsockopt SOL_SOCKET SO_GET_FILTER"); 160 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", NULL, [%u->0]) " 161 "= 0\n", 162 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), 163 BPF_MAXINSNS); 164 165 /* getsockopt NULL optlen - EFAULT */ 166 rc = get_filter(fd, NULL, NULL); 167 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", NULL, NULL) " 168 "= %s\n", 169 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), errstr); 170 171 /* attach a filter */ 172 rc = set_filter(fd, prog, sizeof(*prog)); 173 if (rc) 174 perror_msg_and_skip("setsockopt SOL_SOCKET SO_ATTACH_FILTER"); 175 printf("setsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", {len=%u, filter=", 176 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_ATTACH_FILTER), 177 prog->len); 178 print_filter(); 179 printf("}, %u) = 0\n", (unsigned int) sizeof(*prog)); 180 181 /* setsockopt optlen is too small - EINVAL */ 182 rc = set_filter(fd, prog, sizeof(*prog) - 4); 183 printf("setsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, %u) = %s\n", 184 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_ATTACH_FILTER), prog, 185 (unsigned int) sizeof(*prog) - 4, errstr); 186 187 #ifdef SO_ATTACH_REUSEPORT_CBPF 188 rc = setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, 189 prog, sizeof(*prog)); 190 errstr = sprintrc(rc); 191 printf("setsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", {len=%u, filter=", 192 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_ATTACH_REUSEPORT_CBPF), 193 prog->len); 194 print_filter(); 195 printf("}, %u) = %s\n", (unsigned int) sizeof(*prog), errstr); 196 #endif 197 198 /* query sock_filter program length -> ARRAY_SIZE(bpf_filter) */ 199 *len = 0; 200 rc = get_filter(fd, efault, len); 201 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, [0->%u]) " 202 "= %s\n", 203 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), efault, 204 (unsigned int) ARRAY_SIZE(bpf_filter), errstr); 205 206 /* getsockopt optlen is too small - EINVAL */ 207 *len = ARRAY_SIZE(bpf_filter) - 1; 208 rc = get_filter(fd, efault, len); 209 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, [%u]) = %s\n", 210 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), efault, 211 (unsigned int) ARRAY_SIZE(bpf_filter) - 1, errstr); 212 213 /* getsockopt optval EFAULT */ 214 *len = ARRAY_SIZE(bpf_filter); 215 rc = get_filter(fd, filter + 1, len); 216 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, [%u]) = %s\n", 217 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), 218 filter + 1, (unsigned int) ARRAY_SIZE(bpf_filter), errstr); 219 220 /* getsockopt optlen is too large - truncated */ 221 *len = ARRAY_SIZE(bpf_filter) + 1; 222 rc = get_filter(fd, filter, len); 223 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", ", 224 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER)); 225 print_filter(); 226 printf(", [%u->%d]) = %s\n", 227 (unsigned int) ARRAY_SIZE(bpf_filter) + 1, *len, errstr); 228 229 puts("+++ exited with 0 +++"); 230 return 0; 231 } 232