Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf

Pull BPF fixes from Daniel Borkmann:

- Fix inlining of bpf_get_smp_processor_id helper for !CONFIG_SMP
systems (Andrea Righi)

- Fix BPF USDT selftests helper code to use asm constraint "m" for
LoongArch (Tiezhu Yang)

- Fix BPF selftest compilation error in get_uprobe_offset when
PROCMAP_QUERY is not defined (Jerome Marchand)

- Fix BPF bpf_skb_change_tail helper when used in context of BPF
sockmap to handle negative skb header offsets (Cong Wang)

- Several fixes to BPF sockmap code, among others, in the area of
socket buffer accounting (Levi Zim, Zijian Zhang, Cong Wang)

* tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf:
selftests/bpf: Test bpf_skb_change_tail() in TC ingress
selftests/bpf: Introduce socket_helpers.h for TC tests
selftests/bpf: Add a BPF selftest for bpf_skb_change_tail()
bpf: Check negative offsets in __bpf_skb_min_len()
tcp_bpf: Fix copied value in tcp_bpf_sendmsg
skmsg: Return copied bytes in sk_msg_memcopy_from_iter
tcp_bpf: Add sk_rmem_alloc related logic for tcp_bpf ingress redirection
tcp_bpf: Charge receive socket buffer in bpf_tcp_ingress()
selftests/bpf: Fix compilation error in get_uprobe_offset()
selftests/bpf: Use asm constraint "m" for LoongArch
bpf: Fix bpf_get_smp_processor_id() on !CONFIG_SMP

+712 -405
+8 -3
include/linux/skmsg.h
··· 317 317 kfree_skb(skb); 318 318 } 319 319 320 - static inline void sk_psock_queue_msg(struct sk_psock *psock, 320 + static inline bool sk_psock_queue_msg(struct sk_psock *psock, 321 321 struct sk_msg *msg) 322 322 { 323 + bool ret; 324 + 323 325 spin_lock_bh(&psock->ingress_lock); 324 - if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) 326 + if (sk_psock_test_state(psock, SK_PSOCK_TX_ENABLED)) { 325 327 list_add_tail(&msg->list, &psock->ingress_msg); 326 - else { 328 + ret = true; 329 + } else { 327 330 sk_msg_free(psock->sk, msg); 328 331 kfree(msg); 332 + ret = false; 329 333 } 330 334 spin_unlock_bh(&psock->ingress_lock); 335 + return ret; 331 336 } 332 337 333 338 static inline struct sk_msg *sk_psock_dequeue_msg(struct sk_psock *psock)
+8 -2
include/net/sock.h
··· 1527 1527 } 1528 1528 1529 1529 static inline bool 1530 - sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, int size) 1530 + __sk_rmem_schedule(struct sock *sk, int size, bool pfmemalloc) 1531 1531 { 1532 1532 int delta; 1533 1533 ··· 1535 1535 return true; 1536 1536 delta = size - sk->sk_forward_alloc; 1537 1537 return delta <= 0 || __sk_mem_schedule(sk, delta, SK_MEM_RECV) || 1538 - skb_pfmemalloc(skb); 1538 + pfmemalloc; 1539 + } 1540 + 1541 + static inline bool 1542 + sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, int size) 1543 + { 1544 + return __sk_rmem_schedule(sk, size, skb_pfmemalloc(skb)); 1539 1545 } 1540 1546 1541 1547 static inline int sk_unused_reserved_mem(const struct sock *sk)
+5 -1
kernel/bpf/verifier.c
··· 21281 21281 * changed in some incompatible and hard to support 21282 21282 * way, it's fine to back out this inlining logic 21283 21283 */ 21284 + #ifdef CONFIG_SMP 21284 21285 insn_buf[0] = BPF_MOV32_IMM(BPF_REG_0, (u32)(unsigned long)&pcpu_hot.cpu_number); 21285 21286 insn_buf[1] = BPF_MOV64_PERCPU_REG(BPF_REG_0, BPF_REG_0); 21286 21287 insn_buf[2] = BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0); 21287 21288 cnt = 3; 21288 - 21289 + #else 21290 + insn_buf[0] = BPF_ALU32_REG(BPF_XOR, BPF_REG_0, BPF_REG_0); 21291 + cnt = 1; 21292 + #endif 21289 21293 new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); 21290 21294 if (!new_prog) 21291 21295 return -ENOMEM;
+15 -6
net/core/filter.c
··· 3734 3734 3735 3735 static u32 __bpf_skb_min_len(const struct sk_buff *skb) 3736 3736 { 3737 - u32 min_len = skb_network_offset(skb); 3737 + int offset = skb_network_offset(skb); 3738 + u32 min_len = 0; 3738 3739 3739 - if (skb_transport_header_was_set(skb)) 3740 - min_len = skb_transport_offset(skb); 3741 - if (skb->ip_summed == CHECKSUM_PARTIAL) 3742 - min_len = skb_checksum_start_offset(skb) + 3743 - skb->csum_offset + sizeof(__sum16); 3740 + if (offset > 0) 3741 + min_len = offset; 3742 + if (skb_transport_header_was_set(skb)) { 3743 + offset = skb_transport_offset(skb); 3744 + if (offset > 0) 3745 + min_len = offset; 3746 + } 3747 + if (skb->ip_summed == CHECKSUM_PARTIAL) { 3748 + offset = skb_checksum_start_offset(skb) + 3749 + skb->csum_offset + sizeof(__sum16); 3750 + if (offset > 0) 3751 + min_len = offset; 3752 + } 3744 3753 return min_len; 3745 3754 } 3746 3755
+8 -3
net/core/skmsg.c
··· 369 369 struct sk_msg *msg, u32 bytes) 370 370 { 371 371 int ret = -ENOSPC, i = msg->sg.curr; 372 + u32 copy, buf_size, copied = 0; 372 373 struct scatterlist *sge; 373 - u32 copy, buf_size; 374 374 void *to; 375 375 376 376 do { ··· 397 397 goto out; 398 398 } 399 399 bytes -= copy; 400 + copied += copy; 400 401 if (!bytes) 401 402 break; 402 403 msg->sg.copybreak = 0; ··· 405 404 } while (i != msg->sg.end); 406 405 out: 407 406 msg->sg.curr = i; 408 - return ret; 407 + return (ret < 0) ? ret : copied; 409 408 } 410 409 EXPORT_SYMBOL_GPL(sk_msg_memcopy_from_iter); 411 410 ··· 446 445 if (likely(!peek)) { 447 446 sge->offset += copy; 448 447 sge->length -= copy; 449 - if (!msg_rx->skb) 448 + if (!msg_rx->skb) { 450 449 sk_mem_uncharge(sk, copy); 450 + atomic_sub(copy, &sk->sk_rmem_alloc); 451 + } 451 452 msg_rx->sg.size -= copy; 452 453 453 454 if (!sge->length) { ··· 775 772 776 773 list_for_each_entry_safe(msg, tmp, &psock->ingress_msg, list) { 777 774 list_del(&msg->list); 775 + if (!msg->skb) 776 + atomic_sub(msg->sg.size, &psock->sk->sk_rmem_alloc); 778 777 sk_msg_free(psock->sk, msg); 779 778 kfree(msg); 780 779 }
+8 -6
net/ipv4/tcp_bpf.c
··· 49 49 sge = sk_msg_elem(msg, i); 50 50 size = (apply && apply_bytes < sge->length) ? 51 51 apply_bytes : sge->length; 52 - if (!sk_wmem_schedule(sk, size)) { 52 + if (!__sk_rmem_schedule(sk, size, false)) { 53 53 if (!copied) 54 54 ret = -ENOMEM; 55 55 break; 56 56 } 57 57 58 58 sk_mem_charge(sk, size); 59 + atomic_add(size, &sk->sk_rmem_alloc); 59 60 sk_msg_xfer(tmp, msg, i, size); 60 61 copied += size; 61 62 if (sge->length) ··· 75 74 76 75 if (!ret) { 77 76 msg->sg.start = i; 78 - sk_psock_queue_msg(psock, tmp); 77 + if (!sk_psock_queue_msg(psock, tmp)) 78 + atomic_sub(copied, &sk->sk_rmem_alloc); 79 79 sk_psock_data_ready(sk, psock); 80 80 } else { 81 81 sk_msg_free(sk, tmp); ··· 495 493 static int tcp_bpf_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) 496 494 { 497 495 struct sk_msg tmp, *msg_tx = NULL; 498 - int copied = 0, err = 0; 496 + int copied = 0, err = 0, ret = 0; 499 497 struct sk_psock *psock; 500 498 long timeo; 501 499 int flags; ··· 538 536 copy = msg_tx->sg.size - osize; 539 537 } 540 538 541 - err = sk_msg_memcopy_from_iter(sk, &msg->msg_iter, msg_tx, 539 + ret = sk_msg_memcopy_from_iter(sk, &msg->msg_iter, msg_tx, 542 540 copy); 543 - if (err < 0) { 541 + if (ret < 0) { 544 542 sk_msg_trim(sk, msg_tx, osize); 545 543 goto out_err; 546 544 } 547 545 548 - copied += copy; 546 + copied += ret; 549 547 if (psock->cork_bytes) { 550 548 if (size > psock->cork_bytes) 551 549 psock->cork_bytes = 0;
+394
tools/testing/selftests/bpf/prog_tests/socket_helpers.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #ifndef __SOCKET_HELPERS__ 4 + #define __SOCKET_HELPERS__ 5 + 6 + #include <linux/vm_sockets.h> 7 + 8 + /* include/linux/net.h */ 9 + #define SOCK_TYPE_MASK 0xf 10 + 11 + #define IO_TIMEOUT_SEC 30 12 + #define MAX_STRERR_LEN 256 13 + 14 + /* workaround for older vm_sockets.h */ 15 + #ifndef VMADDR_CID_LOCAL 16 + #define VMADDR_CID_LOCAL 1 17 + #endif 18 + 19 + /* include/linux/cleanup.h */ 20 + #define __get_and_null(p, nullvalue) \ 21 + ({ \ 22 + __auto_type __ptr = &(p); \ 23 + __auto_type __val = *__ptr; \ 24 + *__ptr = nullvalue; \ 25 + __val; \ 26 + }) 27 + 28 + #define take_fd(fd) __get_and_null(fd, -EBADF) 29 + 30 + /* Wrappers that fail the test on error and report it. */ 31 + 32 + #define _FAIL(errnum, fmt...) \ 33 + ({ \ 34 + error_at_line(0, (errnum), __func__, __LINE__, fmt); \ 35 + CHECK_FAIL(true); \ 36 + }) 37 + #define FAIL(fmt...) _FAIL(0, fmt) 38 + #define FAIL_ERRNO(fmt...) _FAIL(errno, fmt) 39 + #define FAIL_LIBBPF(err, msg) \ 40 + ({ \ 41 + char __buf[MAX_STRERR_LEN]; \ 42 + libbpf_strerror((err), __buf, sizeof(__buf)); \ 43 + FAIL("%s: %s", (msg), __buf); \ 44 + }) 45 + 46 + 47 + #define xaccept_nonblock(fd, addr, len) \ 48 + ({ \ 49 + int __ret = \ 50 + accept_timeout((fd), (addr), (len), IO_TIMEOUT_SEC); \ 51 + if (__ret == -1) \ 52 + FAIL_ERRNO("accept"); \ 53 + __ret; \ 54 + }) 55 + 56 + #define xbind(fd, addr, len) \ 57 + ({ \ 58 + int __ret = bind((fd), (addr), (len)); \ 59 + if (__ret == -1) \ 60 + FAIL_ERRNO("bind"); \ 61 + __ret; \ 62 + }) 63 + 64 + #define xclose(fd) \ 65 + ({ \ 66 + int __ret = close((fd)); \ 67 + if (__ret == -1) \ 68 + FAIL_ERRNO("close"); \ 69 + __ret; \ 70 + }) 71 + 72 + #define xconnect(fd, addr, len) \ 73 + ({ \ 74 + int __ret = connect((fd), (addr), (len)); \ 75 + if (__ret == -1) \ 76 + FAIL_ERRNO("connect"); \ 77 + __ret; \ 78 + }) 79 + 80 + #define xgetsockname(fd, addr, len) \ 81 + ({ \ 82 + int __ret = getsockname((fd), (addr), (len)); \ 83 + if (__ret == -1) \ 84 + FAIL_ERRNO("getsockname"); \ 85 + __ret; \ 86 + }) 87 + 88 + #define xgetsockopt(fd, level, name, val, len) \ 89 + ({ \ 90 + int __ret = getsockopt((fd), (level), (name), (val), (len)); \ 91 + if (__ret == -1) \ 92 + FAIL_ERRNO("getsockopt(" #name ")"); \ 93 + __ret; \ 94 + }) 95 + 96 + #define xlisten(fd, backlog) \ 97 + ({ \ 98 + int __ret = listen((fd), (backlog)); \ 99 + if (__ret == -1) \ 100 + FAIL_ERRNO("listen"); \ 101 + __ret; \ 102 + }) 103 + 104 + #define xsetsockopt(fd, level, name, val, len) \ 105 + ({ \ 106 + int __ret = setsockopt((fd), (level), (name), (val), (len)); \ 107 + if (__ret == -1) \ 108 + FAIL_ERRNO("setsockopt(" #name ")"); \ 109 + __ret; \ 110 + }) 111 + 112 + #define xsend(fd, buf, len, flags) \ 113 + ({ \ 114 + ssize_t __ret = send((fd), (buf), (len), (flags)); \ 115 + if (__ret == -1) \ 116 + FAIL_ERRNO("send"); \ 117 + __ret; \ 118 + }) 119 + 120 + #define xrecv_nonblock(fd, buf, len, flags) \ 121 + ({ \ 122 + ssize_t __ret = recv_timeout((fd), (buf), (len), (flags), \ 123 + IO_TIMEOUT_SEC); \ 124 + if (__ret == -1) \ 125 + FAIL_ERRNO("recv"); \ 126 + __ret; \ 127 + }) 128 + 129 + #define xsocket(family, sotype, flags) \ 130 + ({ \ 131 + int __ret = socket(family, sotype, flags); \ 132 + if (__ret == -1) \ 133 + FAIL_ERRNO("socket"); \ 134 + __ret; \ 135 + }) 136 + 137 + static inline void close_fd(int *fd) 138 + { 139 + if (*fd >= 0) 140 + xclose(*fd); 141 + } 142 + 143 + #define __close_fd __attribute__((cleanup(close_fd))) 144 + 145 + static inline struct sockaddr *sockaddr(struct sockaddr_storage *ss) 146 + { 147 + return (struct sockaddr *)ss; 148 + } 149 + 150 + static inline void init_addr_loopback4(struct sockaddr_storage *ss, 151 + socklen_t *len) 152 + { 153 + struct sockaddr_in *addr4 = memset(ss, 0, sizeof(*ss)); 154 + 155 + addr4->sin_family = AF_INET; 156 + addr4->sin_port = 0; 157 + addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 158 + *len = sizeof(*addr4); 159 + } 160 + 161 + static inline void init_addr_loopback6(struct sockaddr_storage *ss, 162 + socklen_t *len) 163 + { 164 + struct sockaddr_in6 *addr6 = memset(ss, 0, sizeof(*ss)); 165 + 166 + addr6->sin6_family = AF_INET6; 167 + addr6->sin6_port = 0; 168 + addr6->sin6_addr = in6addr_loopback; 169 + *len = sizeof(*addr6); 170 + } 171 + 172 + static inline void init_addr_loopback_vsock(struct sockaddr_storage *ss, 173 + socklen_t *len) 174 + { 175 + struct sockaddr_vm *addr = memset(ss, 0, sizeof(*ss)); 176 + 177 + addr->svm_family = AF_VSOCK; 178 + addr->svm_port = VMADDR_PORT_ANY; 179 + addr->svm_cid = VMADDR_CID_LOCAL; 180 + *len = sizeof(*addr); 181 + } 182 + 183 + static inline void init_addr_loopback(int family, struct sockaddr_storage *ss, 184 + socklen_t *len) 185 + { 186 + switch (family) { 187 + case AF_INET: 188 + init_addr_loopback4(ss, len); 189 + return; 190 + case AF_INET6: 191 + init_addr_loopback6(ss, len); 192 + return; 193 + case AF_VSOCK: 194 + init_addr_loopback_vsock(ss, len); 195 + return; 196 + default: 197 + FAIL("unsupported address family %d", family); 198 + } 199 + } 200 + 201 + static inline int enable_reuseport(int s, int progfd) 202 + { 203 + int err, one = 1; 204 + 205 + err = xsetsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 206 + if (err) 207 + return -1; 208 + err = xsetsockopt(s, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &progfd, 209 + sizeof(progfd)); 210 + if (err) 211 + return -1; 212 + 213 + return 0; 214 + } 215 + 216 + static inline int socket_loopback_reuseport(int family, int sotype, int progfd) 217 + { 218 + struct sockaddr_storage addr; 219 + socklen_t len = 0; 220 + int err, s; 221 + 222 + init_addr_loopback(family, &addr, &len); 223 + 224 + s = xsocket(family, sotype, 0); 225 + if (s == -1) 226 + return -1; 227 + 228 + if (progfd >= 0) 229 + enable_reuseport(s, progfd); 230 + 231 + err = xbind(s, sockaddr(&addr), len); 232 + if (err) 233 + goto close; 234 + 235 + if (sotype & SOCK_DGRAM) 236 + return s; 237 + 238 + err = xlisten(s, SOMAXCONN); 239 + if (err) 240 + goto close; 241 + 242 + return s; 243 + close: 244 + xclose(s); 245 + return -1; 246 + } 247 + 248 + static inline int socket_loopback(int family, int sotype) 249 + { 250 + return socket_loopback_reuseport(family, sotype, -1); 251 + } 252 + 253 + static inline int poll_connect(int fd, unsigned int timeout_sec) 254 + { 255 + struct timeval timeout = { .tv_sec = timeout_sec }; 256 + fd_set wfds; 257 + int r, eval; 258 + socklen_t esize = sizeof(eval); 259 + 260 + FD_ZERO(&wfds); 261 + FD_SET(fd, &wfds); 262 + 263 + r = select(fd + 1, NULL, &wfds, NULL, &timeout); 264 + if (r == 0) 265 + errno = ETIME; 266 + if (r != 1) 267 + return -1; 268 + 269 + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &eval, &esize) < 0) 270 + return -1; 271 + if (eval != 0) { 272 + errno = eval; 273 + return -1; 274 + } 275 + 276 + return 0; 277 + } 278 + 279 + static inline int poll_read(int fd, unsigned int timeout_sec) 280 + { 281 + struct timeval timeout = { .tv_sec = timeout_sec }; 282 + fd_set rfds; 283 + int r; 284 + 285 + FD_ZERO(&rfds); 286 + FD_SET(fd, &rfds); 287 + 288 + r = select(fd + 1, &rfds, NULL, NULL, &timeout); 289 + if (r == 0) 290 + errno = ETIME; 291 + 292 + return r == 1 ? 0 : -1; 293 + } 294 + 295 + static inline int accept_timeout(int fd, struct sockaddr *addr, socklen_t *len, 296 + unsigned int timeout_sec) 297 + { 298 + if (poll_read(fd, timeout_sec)) 299 + return -1; 300 + 301 + return accept(fd, addr, len); 302 + } 303 + 304 + static inline int recv_timeout(int fd, void *buf, size_t len, int flags, 305 + unsigned int timeout_sec) 306 + { 307 + if (poll_read(fd, timeout_sec)) 308 + return -1; 309 + 310 + return recv(fd, buf, len, flags); 311 + } 312 + 313 + 314 + static inline int create_pair(int family, int sotype, int *p0, int *p1) 315 + { 316 + __close_fd int s, c = -1, p = -1; 317 + struct sockaddr_storage addr; 318 + socklen_t len = sizeof(addr); 319 + int err; 320 + 321 + s = socket_loopback(family, sotype); 322 + if (s < 0) 323 + return s; 324 + 325 + err = xgetsockname(s, sockaddr(&addr), &len); 326 + if (err) 327 + return err; 328 + 329 + c = xsocket(family, sotype, 0); 330 + if (c < 0) 331 + return c; 332 + 333 + err = connect(c, sockaddr(&addr), len); 334 + if (err) { 335 + if (errno != EINPROGRESS) { 336 + FAIL_ERRNO("connect"); 337 + return err; 338 + } 339 + 340 + err = poll_connect(c, IO_TIMEOUT_SEC); 341 + if (err) { 342 + FAIL_ERRNO("poll_connect"); 343 + return err; 344 + } 345 + } 346 + 347 + switch (sotype & SOCK_TYPE_MASK) { 348 + case SOCK_DGRAM: 349 + err = xgetsockname(c, sockaddr(&addr), &len); 350 + if (err) 351 + return err; 352 + 353 + err = xconnect(s, sockaddr(&addr), len); 354 + if (err) 355 + return err; 356 + 357 + *p0 = take_fd(s); 358 + break; 359 + case SOCK_STREAM: 360 + case SOCK_SEQPACKET: 361 + p = xaccept_nonblock(s, NULL, NULL); 362 + if (p < 0) 363 + return p; 364 + 365 + *p0 = take_fd(p); 366 + break; 367 + default: 368 + FAIL("Unsupported socket type %#x", sotype); 369 + return -EOPNOTSUPP; 370 + } 371 + 372 + *p1 = take_fd(c); 373 + return 0; 374 + } 375 + 376 + static inline int create_socket_pairs(int family, int sotype, int *c0, int *c1, 377 + int *p0, int *p1) 378 + { 379 + int err; 380 + 381 + err = create_pair(family, sotype, c0, p0); 382 + if (err) 383 + return err; 384 + 385 + err = create_pair(family, sotype, c1, p1); 386 + if (err) { 387 + close(*c0); 388 + close(*p0); 389 + } 390 + 391 + return err; 392 + } 393 + 394 + #endif // __SOCKET_HELPERS__
+51
tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
··· 12 12 #include "test_sockmap_progs_query.skel.h" 13 13 #include "test_sockmap_pass_prog.skel.h" 14 14 #include "test_sockmap_drop_prog.skel.h" 15 + #include "test_sockmap_change_tail.skel.h" 15 16 #include "bpf_iter_sockmap.skel.h" 16 17 17 18 #include "sockmap_helpers.h" ··· 644 643 test_sockmap_drop_prog__destroy(drop); 645 644 } 646 645 646 + static void test_sockmap_skb_verdict_change_tail(void) 647 + { 648 + struct test_sockmap_change_tail *skel; 649 + int err, map, verdict; 650 + int c1, p1, sent, recvd; 651 + int zero = 0; 652 + char buf[2]; 653 + 654 + skel = test_sockmap_change_tail__open_and_load(); 655 + if (!ASSERT_OK_PTR(skel, "open_and_load")) 656 + return; 657 + verdict = bpf_program__fd(skel->progs.prog_skb_verdict); 658 + map = bpf_map__fd(skel->maps.sock_map_rx); 659 + 660 + err = bpf_prog_attach(verdict, map, BPF_SK_SKB_STREAM_VERDICT, 0); 661 + if (!ASSERT_OK(err, "bpf_prog_attach")) 662 + goto out; 663 + err = create_pair(AF_INET, SOCK_STREAM, &c1, &p1); 664 + if (!ASSERT_OK(err, "create_pair()")) 665 + goto out; 666 + err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST); 667 + if (!ASSERT_OK(err, "bpf_map_update_elem(c1)")) 668 + goto out_close; 669 + sent = xsend(p1, "Tr", 2, 0); 670 + ASSERT_EQ(sent, 2, "xsend(p1)"); 671 + recvd = recv(c1, buf, 2, 0); 672 + ASSERT_EQ(recvd, 1, "recv(c1)"); 673 + ASSERT_EQ(skel->data->change_tail_ret, 0, "change_tail_ret"); 674 + 675 + sent = xsend(p1, "G", 1, 0); 676 + ASSERT_EQ(sent, 1, "xsend(p1)"); 677 + recvd = recv(c1, buf, 2, 0); 678 + ASSERT_EQ(recvd, 2, "recv(c1)"); 679 + ASSERT_EQ(skel->data->change_tail_ret, 0, "change_tail_ret"); 680 + 681 + sent = xsend(p1, "E", 1, 0); 682 + ASSERT_EQ(sent, 1, "xsend(p1)"); 683 + recvd = recv(c1, buf, 1, 0); 684 + ASSERT_EQ(recvd, 1, "recv(c1)"); 685 + ASSERT_EQ(skel->data->change_tail_ret, -EINVAL, "change_tail_ret"); 686 + 687 + out_close: 688 + close(c1); 689 + close(p1); 690 + out: 691 + test_sockmap_change_tail__destroy(skel); 692 + } 693 + 647 694 static void test_sockmap_skb_verdict_peek_helper(int map) 648 695 { 649 696 int err, c1, p1, zero = 0, sent, recvd, avail; ··· 1107 1058 test_sockmap_skb_verdict_fionread(true); 1108 1059 if (test__start_subtest("sockmap skb_verdict fionread on drop")) 1109 1060 test_sockmap_skb_verdict_fionread(false); 1061 + if (test__start_subtest("sockmap skb_verdict change tail")) 1062 + test_sockmap_skb_verdict_change_tail(); 1110 1063 if (test__start_subtest("sockmap skb_verdict msg_f_peek")) 1111 1064 test_sockmap_skb_verdict_peek(); 1112 1065 if (test__start_subtest("sockmap skb_verdict msg_f_peek with link"))
+1 -384
tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h
··· 1 1 #ifndef __SOCKMAP_HELPERS__ 2 2 #define __SOCKMAP_HELPERS__ 3 3 4 - #include <linux/vm_sockets.h> 4 + #include "socket_helpers.h" 5 5 6 - /* include/linux/net.h */ 7 - #define SOCK_TYPE_MASK 0xf 8 - 9 - #define IO_TIMEOUT_SEC 30 10 - #define MAX_STRERR_LEN 256 11 6 #define MAX_TEST_NAME 80 12 7 13 - /* workaround for older vm_sockets.h */ 14 - #ifndef VMADDR_CID_LOCAL 15 - #define VMADDR_CID_LOCAL 1 16 - #endif 17 - 18 8 #define __always_unused __attribute__((__unused__)) 19 - 20 - /* include/linux/cleanup.h */ 21 - #define __get_and_null(p, nullvalue) \ 22 - ({ \ 23 - __auto_type __ptr = &(p); \ 24 - __auto_type __val = *__ptr; \ 25 - *__ptr = nullvalue; \ 26 - __val; \ 27 - }) 28 - 29 - #define take_fd(fd) __get_and_null(fd, -EBADF) 30 - 31 - #define _FAIL(errnum, fmt...) \ 32 - ({ \ 33 - error_at_line(0, (errnum), __func__, __LINE__, fmt); \ 34 - CHECK_FAIL(true); \ 35 - }) 36 - #define FAIL(fmt...) _FAIL(0, fmt) 37 - #define FAIL_ERRNO(fmt...) _FAIL(errno, fmt) 38 - #define FAIL_LIBBPF(err, msg) \ 39 - ({ \ 40 - char __buf[MAX_STRERR_LEN]; \ 41 - libbpf_strerror((err), __buf, sizeof(__buf)); \ 42 - FAIL("%s: %s", (msg), __buf); \ 43 - }) 44 - 45 - /* Wrappers that fail the test on error and report it. */ 46 - 47 - #define xaccept_nonblock(fd, addr, len) \ 48 - ({ \ 49 - int __ret = \ 50 - accept_timeout((fd), (addr), (len), IO_TIMEOUT_SEC); \ 51 - if (__ret == -1) \ 52 - FAIL_ERRNO("accept"); \ 53 - __ret; \ 54 - }) 55 - 56 - #define xbind(fd, addr, len) \ 57 - ({ \ 58 - int __ret = bind((fd), (addr), (len)); \ 59 - if (__ret == -1) \ 60 - FAIL_ERRNO("bind"); \ 61 - __ret; \ 62 - }) 63 - 64 - #define xclose(fd) \ 65 - ({ \ 66 - int __ret = close((fd)); \ 67 - if (__ret == -1) \ 68 - FAIL_ERRNO("close"); \ 69 - __ret; \ 70 - }) 71 - 72 - #define xconnect(fd, addr, len) \ 73 - ({ \ 74 - int __ret = connect((fd), (addr), (len)); \ 75 - if (__ret == -1) \ 76 - FAIL_ERRNO("connect"); \ 77 - __ret; \ 78 - }) 79 - 80 - #define xgetsockname(fd, addr, len) \ 81 - ({ \ 82 - int __ret = getsockname((fd), (addr), (len)); \ 83 - if (__ret == -1) \ 84 - FAIL_ERRNO("getsockname"); \ 85 - __ret; \ 86 - }) 87 - 88 - #define xgetsockopt(fd, level, name, val, len) \ 89 - ({ \ 90 - int __ret = getsockopt((fd), (level), (name), (val), (len)); \ 91 - if (__ret == -1) \ 92 - FAIL_ERRNO("getsockopt(" #name ")"); \ 93 - __ret; \ 94 - }) 95 - 96 - #define xlisten(fd, backlog) \ 97 - ({ \ 98 - int __ret = listen((fd), (backlog)); \ 99 - if (__ret == -1) \ 100 - FAIL_ERRNO("listen"); \ 101 - __ret; \ 102 - }) 103 - 104 - #define xsetsockopt(fd, level, name, val, len) \ 105 - ({ \ 106 - int __ret = setsockopt((fd), (level), (name), (val), (len)); \ 107 - if (__ret == -1) \ 108 - FAIL_ERRNO("setsockopt(" #name ")"); \ 109 - __ret; \ 110 - }) 111 - 112 - #define xsend(fd, buf, len, flags) \ 113 - ({ \ 114 - ssize_t __ret = send((fd), (buf), (len), (flags)); \ 115 - if (__ret == -1) \ 116 - FAIL_ERRNO("send"); \ 117 - __ret; \ 118 - }) 119 - 120 - #define xrecv_nonblock(fd, buf, len, flags) \ 121 - ({ \ 122 - ssize_t __ret = recv_timeout((fd), (buf), (len), (flags), \ 123 - IO_TIMEOUT_SEC); \ 124 - if (__ret == -1) \ 125 - FAIL_ERRNO("recv"); \ 126 - __ret; \ 127 - }) 128 - 129 - #define xsocket(family, sotype, flags) \ 130 - ({ \ 131 - int __ret = socket(family, sotype, flags); \ 132 - if (__ret == -1) \ 133 - FAIL_ERRNO("socket"); \ 134 - __ret; \ 135 - }) 136 9 137 10 #define xbpf_map_delete_elem(fd, key) \ 138 11 ({ \ ··· 66 193 __ret; \ 67 194 }) 68 195 69 - static inline void close_fd(int *fd) 70 - { 71 - if (*fd >= 0) 72 - xclose(*fd); 73 - } 74 - 75 - #define __close_fd __attribute__((cleanup(close_fd))) 76 - 77 - static inline int poll_connect(int fd, unsigned int timeout_sec) 78 - { 79 - struct timeval timeout = { .tv_sec = timeout_sec }; 80 - fd_set wfds; 81 - int r, eval; 82 - socklen_t esize = sizeof(eval); 83 - 84 - FD_ZERO(&wfds); 85 - FD_SET(fd, &wfds); 86 - 87 - r = select(fd + 1, NULL, &wfds, NULL, &timeout); 88 - if (r == 0) 89 - errno = ETIME; 90 - if (r != 1) 91 - return -1; 92 - 93 - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &eval, &esize) < 0) 94 - return -1; 95 - if (eval != 0) { 96 - errno = eval; 97 - return -1; 98 - } 99 - 100 - return 0; 101 - } 102 - 103 - static inline int poll_read(int fd, unsigned int timeout_sec) 104 - { 105 - struct timeval timeout = { .tv_sec = timeout_sec }; 106 - fd_set rfds; 107 - int r; 108 - 109 - FD_ZERO(&rfds); 110 - FD_SET(fd, &rfds); 111 - 112 - r = select(fd + 1, &rfds, NULL, NULL, &timeout); 113 - if (r == 0) 114 - errno = ETIME; 115 - 116 - return r == 1 ? 0 : -1; 117 - } 118 - 119 - static inline int accept_timeout(int fd, struct sockaddr *addr, socklen_t *len, 120 - unsigned int timeout_sec) 121 - { 122 - if (poll_read(fd, timeout_sec)) 123 - return -1; 124 - 125 - return accept(fd, addr, len); 126 - } 127 - 128 - static inline int recv_timeout(int fd, void *buf, size_t len, int flags, 129 - unsigned int timeout_sec) 130 - { 131 - if (poll_read(fd, timeout_sec)) 132 - return -1; 133 - 134 - return recv(fd, buf, len, flags); 135 - } 136 - 137 - static inline void init_addr_loopback4(struct sockaddr_storage *ss, 138 - socklen_t *len) 139 - { 140 - struct sockaddr_in *addr4 = memset(ss, 0, sizeof(*ss)); 141 - 142 - addr4->sin_family = AF_INET; 143 - addr4->sin_port = 0; 144 - addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 145 - *len = sizeof(*addr4); 146 - } 147 - 148 - static inline void init_addr_loopback6(struct sockaddr_storage *ss, 149 - socklen_t *len) 150 - { 151 - struct sockaddr_in6 *addr6 = memset(ss, 0, sizeof(*ss)); 152 - 153 - addr6->sin6_family = AF_INET6; 154 - addr6->sin6_port = 0; 155 - addr6->sin6_addr = in6addr_loopback; 156 - *len = sizeof(*addr6); 157 - } 158 - 159 - static inline void init_addr_loopback_vsock(struct sockaddr_storage *ss, 160 - socklen_t *len) 161 - { 162 - struct sockaddr_vm *addr = memset(ss, 0, sizeof(*ss)); 163 - 164 - addr->svm_family = AF_VSOCK; 165 - addr->svm_port = VMADDR_PORT_ANY; 166 - addr->svm_cid = VMADDR_CID_LOCAL; 167 - *len = sizeof(*addr); 168 - } 169 - 170 - static inline void init_addr_loopback(int family, struct sockaddr_storage *ss, 171 - socklen_t *len) 172 - { 173 - switch (family) { 174 - case AF_INET: 175 - init_addr_loopback4(ss, len); 176 - return; 177 - case AF_INET6: 178 - init_addr_loopback6(ss, len); 179 - return; 180 - case AF_VSOCK: 181 - init_addr_loopback_vsock(ss, len); 182 - return; 183 - default: 184 - FAIL("unsupported address family %d", family); 185 - } 186 - } 187 - 188 - static inline struct sockaddr *sockaddr(struct sockaddr_storage *ss) 189 - { 190 - return (struct sockaddr *)ss; 191 - } 192 - 193 196 static inline int add_to_sockmap(int sock_mapfd, int fd1, int fd2) 194 197 { 195 198 u64 value; ··· 81 332 key = 1; 82 333 value = fd2; 83 334 return xbpf_map_update_elem(sock_mapfd, &key, &value, BPF_NOEXIST); 84 - } 85 - 86 - static inline int enable_reuseport(int s, int progfd) 87 - { 88 - int err, one = 1; 89 - 90 - err = xsetsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 91 - if (err) 92 - return -1; 93 - err = xsetsockopt(s, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &progfd, 94 - sizeof(progfd)); 95 - if (err) 96 - return -1; 97 - 98 - return 0; 99 - } 100 - 101 - static inline int socket_loopback_reuseport(int family, int sotype, int progfd) 102 - { 103 - struct sockaddr_storage addr; 104 - socklen_t len = 0; 105 - int err, s; 106 - 107 - init_addr_loopback(family, &addr, &len); 108 - 109 - s = xsocket(family, sotype, 0); 110 - if (s == -1) 111 - return -1; 112 - 113 - if (progfd >= 0) 114 - enable_reuseport(s, progfd); 115 - 116 - err = xbind(s, sockaddr(&addr), len); 117 - if (err) 118 - goto close; 119 - 120 - if (sotype & SOCK_DGRAM) 121 - return s; 122 - 123 - err = xlisten(s, SOMAXCONN); 124 - if (err) 125 - goto close; 126 - 127 - return s; 128 - close: 129 - xclose(s); 130 - return -1; 131 - } 132 - 133 - static inline int socket_loopback(int family, int sotype) 134 - { 135 - return socket_loopback_reuseport(family, sotype, -1); 136 - } 137 - 138 - static inline int create_pair(int family, int sotype, int *p0, int *p1) 139 - { 140 - __close_fd int s, c = -1, p = -1; 141 - struct sockaddr_storage addr; 142 - socklen_t len = sizeof(addr); 143 - int err; 144 - 145 - s = socket_loopback(family, sotype); 146 - if (s < 0) 147 - return s; 148 - 149 - err = xgetsockname(s, sockaddr(&addr), &len); 150 - if (err) 151 - return err; 152 - 153 - c = xsocket(family, sotype, 0); 154 - if (c < 0) 155 - return c; 156 - 157 - err = connect(c, sockaddr(&addr), len); 158 - if (err) { 159 - if (errno != EINPROGRESS) { 160 - FAIL_ERRNO("connect"); 161 - return err; 162 - } 163 - 164 - err = poll_connect(c, IO_TIMEOUT_SEC); 165 - if (err) { 166 - FAIL_ERRNO("poll_connect"); 167 - return err; 168 - } 169 - } 170 - 171 - switch (sotype & SOCK_TYPE_MASK) { 172 - case SOCK_DGRAM: 173 - err = xgetsockname(c, sockaddr(&addr), &len); 174 - if (err) 175 - return err; 176 - 177 - err = xconnect(s, sockaddr(&addr), len); 178 - if (err) 179 - return err; 180 - 181 - *p0 = take_fd(s); 182 - break; 183 - case SOCK_STREAM: 184 - case SOCK_SEQPACKET: 185 - p = xaccept_nonblock(s, NULL, NULL); 186 - if (p < 0) 187 - return p; 188 - 189 - *p0 = take_fd(p); 190 - break; 191 - default: 192 - FAIL("Unsupported socket type %#x", sotype); 193 - return -EOPNOTSUPP; 194 - } 195 - 196 - *p1 = take_fd(c); 197 - return 0; 198 - } 199 - 200 - static inline int create_socket_pairs(int family, int sotype, int *c0, int *c1, 201 - int *p0, int *p1) 202 - { 203 - int err; 204 - 205 - err = create_pair(family, sotype, c0, p0); 206 - if (err) 207 - return err; 208 - 209 - err = create_pair(family, sotype, c1, p1); 210 - if (err) { 211 - close(*c0); 212 - close(*p0); 213 - } 214 - 215 - return err; 216 335 } 217 336 218 337 #endif // __SOCKMAP_HELPERS__
+62
tools/testing/selftests/bpf/prog_tests/tc_change_tail.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <error.h> 3 + #include <test_progs.h> 4 + #include <linux/pkt_cls.h> 5 + 6 + #include "test_tc_change_tail.skel.h" 7 + #include "socket_helpers.h" 8 + 9 + #define LO_IFINDEX 1 10 + 11 + void test_tc_change_tail(void) 12 + { 13 + LIBBPF_OPTS(bpf_tcx_opts, tcx_opts); 14 + struct test_tc_change_tail *skel = NULL; 15 + struct bpf_link *link; 16 + int c1, p1; 17 + char buf[2]; 18 + int ret; 19 + 20 + skel = test_tc_change_tail__open_and_load(); 21 + if (!ASSERT_OK_PTR(skel, "test_tc_change_tail__open_and_load")) 22 + return; 23 + 24 + link = bpf_program__attach_tcx(skel->progs.change_tail, LO_IFINDEX, 25 + &tcx_opts); 26 + if (!ASSERT_OK_PTR(link, "bpf_program__attach_tcx")) 27 + goto destroy; 28 + 29 + skel->links.change_tail = link; 30 + ret = create_pair(AF_INET, SOCK_DGRAM, &c1, &p1); 31 + if (!ASSERT_OK(ret, "create_pair")) 32 + goto destroy; 33 + 34 + ret = xsend(p1, "Tr", 2, 0); 35 + ASSERT_EQ(ret, 2, "xsend(p1)"); 36 + ret = recv(c1, buf, 2, 0); 37 + ASSERT_EQ(ret, 2, "recv(c1)"); 38 + ASSERT_EQ(skel->data->change_tail_ret, 0, "change_tail_ret"); 39 + 40 + ret = xsend(p1, "G", 1, 0); 41 + ASSERT_EQ(ret, 1, "xsend(p1)"); 42 + ret = recv(c1, buf, 2, 0); 43 + ASSERT_EQ(ret, 1, "recv(c1)"); 44 + ASSERT_EQ(skel->data->change_tail_ret, 0, "change_tail_ret"); 45 + 46 + ret = xsend(p1, "E", 1, 0); 47 + ASSERT_EQ(ret, 1, "xsend(p1)"); 48 + ret = recv(c1, buf, 1, 0); 49 + ASSERT_EQ(ret, 1, "recv(c1)"); 50 + ASSERT_EQ(skel->data->change_tail_ret, -EINVAL, "change_tail_ret"); 51 + 52 + ret = xsend(p1, "Z", 1, 0); 53 + ASSERT_EQ(ret, 1, "xsend(p1)"); 54 + ret = recv(c1, buf, 1, 0); 55 + ASSERT_EQ(ret, 1, "recv(c1)"); 56 + ASSERT_EQ(skel->data->change_tail_ret, -EINVAL, "change_tail_ret"); 57 + 58 + close(c1); 59 + close(p1); 60 + destroy: 61 + test_tc_change_tail__destroy(skel); 62 + }
+40
tools/testing/selftests/bpf/progs/test_sockmap_change_tail.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2024 ByteDance */ 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct { 7 + __uint(type, BPF_MAP_TYPE_SOCKMAP); 8 + __uint(max_entries, 1); 9 + __type(key, int); 10 + __type(value, int); 11 + } sock_map_rx SEC(".maps"); 12 + 13 + long change_tail_ret = 1; 14 + 15 + SEC("sk_skb") 16 + int prog_skb_verdict(struct __sk_buff *skb) 17 + { 18 + char *data, *data_end; 19 + 20 + bpf_skb_pull_data(skb, 1); 21 + data = (char *)(unsigned long)skb->data; 22 + data_end = (char *)(unsigned long)skb->data_end; 23 + 24 + if (data + 1 > data_end) 25 + return SK_PASS; 26 + 27 + if (data[0] == 'T') { /* Trim the packet */ 28 + change_tail_ret = bpf_skb_change_tail(skb, skb->len - 1, 0); 29 + return SK_PASS; 30 + } else if (data[0] == 'G') { /* Grow the packet */ 31 + change_tail_ret = bpf_skb_change_tail(skb, skb->len + 1, 0); 32 + return SK_PASS; 33 + } else if (data[0] == 'E') { /* Error */ 34 + change_tail_ret = bpf_skb_change_tail(skb, 65535, 0); 35 + return SK_PASS; 36 + } 37 + return SK_PASS; 38 + } 39 + 40 + char _license[] SEC("license") = "GPL";
+106
tools/testing/selftests/bpf/progs/test_tc_change_tail.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bpf.h> 3 + #include <bpf/bpf_helpers.h> 4 + #include <linux/if_ether.h> 5 + #include <linux/in.h> 6 + #include <linux/ip.h> 7 + #include <linux/udp.h> 8 + #include <linux/pkt_cls.h> 9 + 10 + long change_tail_ret = 1; 11 + 12 + static __always_inline struct iphdr *parse_ip_header(struct __sk_buff *skb, int *ip_proto) 13 + { 14 + void *data_end = (void *)(long)skb->data_end; 15 + void *data = (void *)(long)skb->data; 16 + struct ethhdr *eth = data; 17 + struct iphdr *iph; 18 + 19 + /* Verify Ethernet header */ 20 + if ((void *)(data + sizeof(*eth)) > data_end) 21 + return NULL; 22 + 23 + /* Skip Ethernet header to get to IP header */ 24 + iph = (void *)(data + sizeof(struct ethhdr)); 25 + 26 + /* Verify IP header */ 27 + if ((void *)(data + sizeof(struct ethhdr) + sizeof(*iph)) > data_end) 28 + return NULL; 29 + 30 + /* Basic IP header validation */ 31 + if (iph->version != 4) /* Only support IPv4 */ 32 + return NULL; 33 + 34 + if (iph->ihl < 5) /* Minimum IP header length */ 35 + return NULL; 36 + 37 + *ip_proto = iph->protocol; 38 + return iph; 39 + } 40 + 41 + static __always_inline struct udphdr *parse_udp_header(struct __sk_buff *skb, struct iphdr *iph) 42 + { 43 + void *data_end = (void *)(long)skb->data_end; 44 + void *hdr = (void *)iph; 45 + struct udphdr *udp; 46 + 47 + /* Calculate UDP header position */ 48 + udp = hdr + (iph->ihl * 4); 49 + hdr = (void *)udp; 50 + 51 + /* Verify UDP header bounds */ 52 + if ((void *)(hdr + sizeof(*udp)) > data_end) 53 + return NULL; 54 + 55 + return udp; 56 + } 57 + 58 + SEC("tc/ingress") 59 + int change_tail(struct __sk_buff *skb) 60 + { 61 + int len = skb->len; 62 + struct udphdr *udp; 63 + struct iphdr *iph; 64 + void *data_end; 65 + char *payload; 66 + int ip_proto; 67 + 68 + bpf_skb_pull_data(skb, len); 69 + 70 + data_end = (void *)(long)skb->data_end; 71 + iph = parse_ip_header(skb, &ip_proto); 72 + if (!iph) 73 + return TCX_PASS; 74 + 75 + if (ip_proto != IPPROTO_UDP) 76 + return TCX_PASS; 77 + 78 + udp = parse_udp_header(skb, iph); 79 + if (!udp) 80 + return TCX_PASS; 81 + 82 + payload = (char *)udp + (sizeof(struct udphdr)); 83 + if (payload + 1 > (char *)data_end) 84 + return TCX_PASS; 85 + 86 + if (payload[0] == 'T') { /* Trim the packet */ 87 + change_tail_ret = bpf_skb_change_tail(skb, len - 1, 0); 88 + if (!change_tail_ret) 89 + bpf_skb_change_tail(skb, len, 0); 90 + return TCX_PASS; 91 + } else if (payload[0] == 'G') { /* Grow the packet */ 92 + change_tail_ret = bpf_skb_change_tail(skb, len + 1, 0); 93 + if (!change_tail_ret) 94 + bpf_skb_change_tail(skb, len, 0); 95 + return TCX_PASS; 96 + } else if (payload[0] == 'E') { /* Error */ 97 + change_tail_ret = bpf_skb_change_tail(skb, 65535, 0); 98 + return TCX_PASS; 99 + } else if (payload[0] == 'Z') { /* Zero */ 100 + change_tail_ret = bpf_skb_change_tail(skb, 0, 0); 101 + return TCX_PASS; 102 + } 103 + return TCX_DROP; 104 + } 105 + 106 + char _license[] SEC("license") = "GPL";
+2
tools/testing/selftests/bpf/sdt.h
··· 102 102 # define STAP_SDT_ARG_CONSTRAINT nZr 103 103 # elif defined __arm__ 104 104 # define STAP_SDT_ARG_CONSTRAINT g 105 + # elif defined __loongarch__ 106 + # define STAP_SDT_ARG_CONSTRAINT nmr 105 107 # else 106 108 # define STAP_SDT_ARG_CONSTRAINT nor 107 109 # endif
+4
tools/testing/selftests/bpf/trace_helpers.c
··· 293 293 return 0; 294 294 } 295 295 #else 296 + # ifndef PROCMAP_QUERY_VMA_EXECUTABLE 297 + # define PROCMAP_QUERY_VMA_EXECUTABLE 0x04 298 + # endif 299 + 296 300 static int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags) 297 301 { 298 302 return -EOPNOTSUPP;