1 /*
2 * Check decoding of socket filters.
3 *
4 * Copyright (c) 2017 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2017-2021 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <netinet/in.h>
16 #include <sys/socket.h>
17 #include <linux/filter.h>
18
19 /* SO_GET_FILTER was introduced by Linux commit v3.8-rc1~139^2~518 */
20 #ifndef SO_GET_FILTER
21 # define SO_GET_FILTER SO_ATTACH_FILTER
22 #endif
23
24 #define HEX_FMT "%#x"
25
26 #define PRINT_STMT(pfx, code_fmt, k_fmt, ...) \
27 printf("%sBPF_STMT(" code_fmt ", " k_fmt ")", pfx, __VA_ARGS__)
28
29 #define PRINT_JUMP(pfx, code_fmt, k, jt, jf, ...) \
30 printf("%sBPF_JUMP(" code_fmt ", %#x, %#x, %#x)", \
31 pfx, __VA_ARGS__, k, jt, jf)
32
33 static const struct sock_filter bpf_filter[] = {
34 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_LL_OFF+4),
35 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_NET_OFF+8),
36 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF+SKF_AD_PROTOCOL),
37 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, IPPROTO_UDP, 0, 5),
38 BPF_STMT(BPF_LD|BPF_W|BPF_LEN, 0),
39 BPF_JUMP(BPF_JMP|BPF_K|BPF_JGE, 100, 0, 3),
40 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 42),
41 BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 'a', 0, 1),
42 BPF_STMT(BPF_RET|BPF_K, -1U),
43 BPF_STMT(BPF_RET|BPF_K, 0)
44 };
45
46 static void
47 print_filter(void)
48 {
49 PRINT_STMT("[", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
50 XLAT_FMT "+4",
51 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS),
52 XLAT_ARGS(SKF_LL_OFF));
53 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
54 XLAT_FMT "+8",
55 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS),
56 XLAT_ARGS(SKF_NET_OFF));
57 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
58 XLAT_FMT "+" XLAT_FMT,
59 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS),
60 XLAT_ARGS(SKF_AD_OFF), XLAT_ARGS(SKF_AD_PROTOCOL));
61 PRINT_JUMP(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
62 IPPROTO_UDP, 0, 5,
63 XLAT_ARGS(BPF_JMP), XLAT_ARGS(BPF_K), XLAT_ARGS(BPF_JEQ));
64 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
65 HEX_FMT,
66 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_W), XLAT_ARGS(BPF_LEN),
67 0);
68 PRINT_JUMP(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
69 100, 0, 3,
70 XLAT_ARGS(BPF_JMP), XLAT_ARGS(BPF_K), XLAT_ARGS(BPF_JGE));
71 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
72 HEX_FMT,
73 XLAT_ARGS(BPF_LD), XLAT_ARGS(BPF_B), XLAT_ARGS(BPF_ABS),
74 42);
75 PRINT_JUMP(", ", XLAT_FMT "|" XLAT_FMT "|" XLAT_FMT,
76 'a', 0, 1,
77 XLAT_ARGS(BPF_JMP), XLAT_ARGS(BPF_K), XLAT_ARGS(BPF_JEQ));
78 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT,
79 HEX_FMT,
80 XLAT_ARGS(BPF_RET), XLAT_ARGS(BPF_K),
81 -1U);
82 PRINT_STMT(", ", XLAT_FMT "|" XLAT_FMT,
83 HEX_FMT,
84 XLAT_ARGS(BPF_RET), XLAT_ARGS(BPF_K),
85 0);
86 putchar(']');
87 }
88
89 static const char *errstr;
90
91 static int
92 get_filter(int fd, void *val, socklen_t *len)
93 {
94 int rc = getsockopt(fd, SOL_SOCKET, SO_GET_FILTER, val, len);
95 errstr = sprintrc(rc);
96 return rc;
97 }
98
99 static int
100 set_filter(int fd, void *val, socklen_t len)
101 {
102 int rc = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, val, len);
103 errstr = sprintrc(rc);
104 return rc;
105 }
106
107 int
108 main(void)
109 {
110 int rc;
111 struct sock_filter *const filter =
112 tail_memdup(bpf_filter, sizeof(bpf_filter));
113 void *const efault = filter + ARRAY_SIZE(bpf_filter);
114 TAIL_ALLOC_OBJECT_CONST_PTR(struct sock_fprog, prog);
115 TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len);
116
117 prog->len = ARRAY_SIZE(bpf_filter);
118 prog->filter = filter;
119
120 int fd = socket(AF_INET, SOCK_DGRAM, 0);
121 if (fd < 0)
122 perror_msg_and_skip("socket AF_INET SOCK_DGRAM");
123
124 /* query sock_filter program length -> 0 */
125 *len = BPF_MAXINSNS;
126 rc = get_filter(fd, NULL, len);
127 if (rc)
128 perror_msg_and_skip("getsockopt SOL_SOCKET SO_GET_FILTER");
129 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", NULL, [%u => 0]) "
130 "= 0\n",
131 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER),
132 BPF_MAXINSNS);
133
134 /* getsockopt NULL optlen - EFAULT */
135 rc = get_filter(fd, NULL, NULL);
136 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", NULL, NULL) "
137 "= %s\n",
138 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), errstr);
139
140 /* attach a filter */
141 rc = set_filter(fd, prog, sizeof(*prog));
142 if (rc)
143 perror_msg_and_skip("setsockopt SOL_SOCKET SO_ATTACH_FILTER");
144 printf("setsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", {len=%u, filter=",
145 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_ATTACH_FILTER),
146 prog->len);
147 print_filter();
148 printf("}, %u) = 0\n", (unsigned int) sizeof(*prog));
149
150 /* setsockopt optlen is too small - EINVAL */
151 rc = set_filter(fd, prog, sizeof(*prog) - 4);
152 printf("setsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, %u) = %s\n",
153 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_ATTACH_FILTER), prog,
154 (unsigned int) sizeof(*prog) - 4, errstr);
155
156 #ifdef SO_ATTACH_REUSEPORT_CBPF
157 rc = setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF,
158 prog, sizeof(*prog));
159 errstr = sprintrc(rc);
160 printf("setsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", {len=%u, filter=",
161 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_ATTACH_REUSEPORT_CBPF),
162 prog->len);
163 print_filter();
164 printf("}, %u) = %s\n", (unsigned int) sizeof(*prog), errstr);
165 #endif
166
167 /* query sock_filter program length -> ARRAY_SIZE(bpf_filter) */
168 *len = 0;
169 rc = get_filter(fd, efault, len);
170 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, [0 => %u]) "
171 "= %s\n",
172 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), efault,
173 (unsigned int) ARRAY_SIZE(bpf_filter), errstr);
174
175 /* getsockopt optlen is too small - EINVAL */
176 *len = ARRAY_SIZE(bpf_filter) - 1;
177 rc = get_filter(fd, efault, len);
178 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, [%u]) = %s\n",
179 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER), efault,
180 (unsigned int) ARRAY_SIZE(bpf_filter) - 1, errstr);
181
182 /* getsockopt optval EFAULT */
183 *len = ARRAY_SIZE(bpf_filter);
184 rc = get_filter(fd, filter + 1, len);
185 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", %p, [%u]) = %s\n",
186 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER),
187 filter + 1, (unsigned int) ARRAY_SIZE(bpf_filter), errstr);
188
189 /* getsockopt optlen is too large - truncated */
190 *len = ARRAY_SIZE(bpf_filter) + 1;
191 rc = get_filter(fd, filter, len);
192 printf("getsockopt(%d, " XLAT_FMT ", " XLAT_FMT ", ",
193 fd, XLAT_ARGS(SOL_SOCKET), XLAT_ARGS(SO_GET_FILTER));
194 print_filter();
195 printf(", [%u => %d]) = %s\n",
196 (unsigned int) ARRAY_SIZE(bpf_filter) + 1, *len, errstr);
197
198 puts("+++ exited with 0 +++");
199 return 0;
200 }