1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright Amazon.com Inc. or its affiliates. */ 3 4 #include <fcntl.h> 5 #include <string.h> 6 #include <unistd.h> 7 8 #include <netinet/in.h> 9 #include <sys/epoll.h> 10 #include <sys/ioctl.h> 11 #include <sys/signalfd.h> 12 #include <sys/socket.h> 13 14 #include "../../kselftest_harness.h" 15 16 #define BUF_SZ 32 17 18 FIXTURE(msg_oob) 19 { 20 int fd[4]; /* 0: AF_UNIX sender 21 * 1: AF_UNIX receiver 22 * 2: TCP sender 23 * 3: TCP receiver 24 */ 25 int signal_fd; 26 int epoll_fd[2]; /* 0: AF_UNIX receiver 27 * 1: TCP receiver 28 */ 29 bool tcp_compliant; 30 }; 31 32 FIXTURE_VARIANT(msg_oob) 33 { 34 bool peek; 35 }; 36 37 FIXTURE_VARIANT_ADD(msg_oob, no_peek) 38 { 39 .peek = false, 40 }; 41 42 FIXTURE_VARIANT_ADD(msg_oob, peek) 43 { 44 .peek = true 45 }; 46 47 static void create_unix_socketpair(struct __test_metadata *_metadata, 48 FIXTURE_DATA(msg_oob) *self) 49 { 50 int ret; 51 52 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, self->fd); 53 ASSERT_EQ(ret, 0); 54 } 55 56 static void create_tcp_socketpair(struct __test_metadata *_metadata, 57 FIXTURE_DATA(msg_oob) *self) 58 { 59 struct sockaddr_in addr; 60 socklen_t addrlen; 61 int listen_fd; 62 int ret; 63 64 listen_fd = socket(AF_INET, SOCK_STREAM, 0); 65 ASSERT_GE(listen_fd, 0); 66 67 ret = listen(listen_fd, -1); 68 ASSERT_EQ(ret, 0); 69 70 addrlen = sizeof(addr); 71 ret = getsockname(listen_fd, (struct sockaddr *)&addr, &addrlen); 72 ASSERT_EQ(ret, 0); 73 74 self->fd[2] = socket(AF_INET, SOCK_STREAM, 0); 75 ASSERT_GE(self->fd[2], 0); 76 77 ret = connect(self->fd[2], (struct sockaddr *)&addr, addrlen); 78 ASSERT_EQ(ret, 0); 79 80 self->fd[3] = accept(listen_fd, (struct sockaddr *)&addr, &addrlen); 81 ASSERT_GE(self->fd[3], 0); 82 83 ret = fcntl(self->fd[3], F_SETFL, O_NONBLOCK); 84 ASSERT_EQ(ret, 0); 85 } 86 87 static void setup_sigurg(struct __test_metadata *_metadata, 88 FIXTURE_DATA(msg_oob) *self) 89 { 90 struct signalfd_siginfo siginfo; 91 int pid = getpid(); 92 sigset_t mask; 93 int i, ret; 94 95 for (i = 0; i < 2; i++) { 96 ret = ioctl(self->fd[i * 2 + 1], FIOSETOWN, &pid); 97 ASSERT_EQ(ret, 0); 98 } 99 100 ret = sigemptyset(&mask); 101 ASSERT_EQ(ret, 0); 102 103 ret = sigaddset(&mask, SIGURG); 104 ASSERT_EQ(ret, 0); 105 106 ret = sigprocmask(SIG_BLOCK, &mask, NULL); 107 ASSERT_EQ(ret, 0); 108 109 self->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK); 110 ASSERT_GE(self->signal_fd, 0); 111 112 ret = read(self->signal_fd, &siginfo, sizeof(siginfo)); 113 ASSERT_EQ(ret, -1); 114 } 115 116 static void setup_epollpri(struct __test_metadata *_metadata, 117 FIXTURE_DATA(msg_oob) *self) 118 { 119 struct epoll_event event = { 120 .events = EPOLLPRI, 121 }; 122 int i; 123 124 for (i = 0; i < 2; i++) { 125 int ret; 126 127 self->epoll_fd[i] = epoll_create1(0); 128 ASSERT_GE(self->epoll_fd[i], 0); 129 130 ret = epoll_ctl(self->epoll_fd[i], EPOLL_CTL_ADD, self->fd[i * 2 + 1], &event); 131 ASSERT_EQ(ret, 0); 132 } 133 } 134 135 static void close_sockets(FIXTURE_DATA(msg_oob) *self) 136 { 137 int i; 138 139 for (i = 0; i < 4; i++) 140 close(self->fd[i]); 141 } 142 143 FIXTURE_SETUP(msg_oob) 144 { 145 create_unix_socketpair(_metadata, self); 146 create_tcp_socketpair(_metadata, self); 147 148 setup_sigurg(_metadata, self); 149 setup_epollpri(_metadata, self); 150 151 self->tcp_compliant = true; 152 } 153 154 FIXTURE_TEARDOWN(msg_oob) 155 { 156 close_sockets(self); 157 } 158 159 static void __epollpair(struct __test_metadata *_metadata, 160 FIXTURE_DATA(msg_oob) *self, 161 bool oob_remaining) 162 { 163 struct epoll_event event[2] = {}; 164 int i, ret[2]; 165 166 for (i = 0; i < 2; i++) 167 ret[i] = epoll_wait(self->epoll_fd[i], &event[i], 1, 0); 168 169 ASSERT_EQ(ret[0], oob_remaining); 170 171 if (self->tcp_compliant) 172 ASSERT_EQ(ret[0], ret[1]); 173 174 if (oob_remaining) { 175 ASSERT_EQ(event[0].events, EPOLLPRI); 176 177 if (self->tcp_compliant) 178 ASSERT_EQ(event[0].events, event[1].events); 179 } 180 } 181 182 static void __sendpair(struct __test_metadata *_metadata, 183 FIXTURE_DATA(msg_oob) *self, 184 const void *buf, size_t len, int flags) 185 { 186 int i, ret[2]; 187 188 for (i = 0; i < 2; i++) { 189 struct signalfd_siginfo siginfo = {}; 190 int bytes; 191 192 ret[i] = send(self->fd[i * 2], buf, len, flags); 193 194 bytes = read(self->signal_fd, &siginfo, sizeof(siginfo)); 195 196 if (flags & MSG_OOB) { 197 ASSERT_EQ(bytes, sizeof(siginfo)); 198 ASSERT_EQ(siginfo.ssi_signo, SIGURG); 199 200 bytes = read(self->signal_fd, &siginfo, sizeof(siginfo)); 201 } 202 203 ASSERT_EQ(bytes, -1); 204 } 205 206 ASSERT_EQ(ret[0], len); 207 ASSERT_EQ(ret[0], ret[1]); 208 } 209 210 static void __recvpair(struct __test_metadata *_metadata, 211 FIXTURE_DATA(msg_oob) *self, 212 const char *expected_buf, int expected_len, 213 int buf_len, int flags) 214 { 215 int i, ret[2], recv_errno[2], expected_errno = 0; 216 char recv_buf[2][BUF_SZ] = {}; 217 bool printed = false; 218 219 ASSERT_GE(BUF_SZ, buf_len); 220 221 errno = 0; 222 223 for (i = 0; i < 2; i++) { 224 ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags); 225 recv_errno[i] = errno; 226 } 227 228 if (expected_len < 0) { 229 expected_errno = -expected_len; 230 expected_len = -1; 231 } 232 233 if (ret[0] != expected_len || recv_errno[0] != expected_errno) { 234 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 235 TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf); 236 237 ASSERT_EQ(ret[0], expected_len); 238 ASSERT_EQ(recv_errno[0], expected_errno); 239 } 240 241 if (ret[0] != ret[1] || recv_errno[0] != recv_errno[1]) { 242 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 243 TH_LOG("TCP :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]); 244 245 printed = true; 246 247 if (self->tcp_compliant) { 248 ASSERT_EQ(ret[0], ret[1]); 249 ASSERT_EQ(recv_errno[0], recv_errno[1]); 250 } 251 } 252 253 if (expected_len >= 0) { 254 int cmp; 255 256 cmp = strncmp(expected_buf, recv_buf[0], expected_len); 257 if (cmp) { 258 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 259 TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf); 260 261 ASSERT_EQ(cmp, 0); 262 } 263 264 cmp = strncmp(recv_buf[0], recv_buf[1], expected_len); 265 if (cmp) { 266 if (!printed) { 267 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]); 268 TH_LOG("TCP :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]); 269 } 270 271 if (self->tcp_compliant) 272 ASSERT_EQ(cmp, 0); 273 } 274 } 275 } 276 277 static void __setinlinepair(struct __test_metadata *_metadata, 278 FIXTURE_DATA(msg_oob) *self) 279 { 280 int i, oob_inline = 1; 281 282 for (i = 0; i < 2; i++) { 283 int ret; 284 285 ret = setsockopt(self->fd[i * 2 + 1], SOL_SOCKET, SO_OOBINLINE, 286 &oob_inline, sizeof(oob_inline)); 287 ASSERT_EQ(ret, 0); 288 } 289 } 290 291 static void __siocatmarkpair(struct __test_metadata *_metadata, 292 FIXTURE_DATA(msg_oob) *self, 293 bool oob_head) 294 { 295 int answ[2] = {}; 296 int i; 297 298 for (i = 0; i < 2; i++) { 299 int ret; 300 301 ret = ioctl(self->fd[i * 2 + 1], SIOCATMARK, &answ[i]); 302 ASSERT_EQ(ret, 0); 303 } 304 305 ASSERT_EQ(answ[0], oob_head); 306 307 if (self->tcp_compliant) 308 ASSERT_EQ(answ[0], answ[1]); 309 } 310 311 #define sendpair(buf, len, flags) \ 312 __sendpair(_metadata, self, buf, len, flags) 313 314 #define recvpair(expected_buf, expected_len, buf_len, flags) \ 315 do { \ 316 if (variant->peek) \ 317 __recvpair(_metadata, self, \ 318 expected_buf, expected_len, \ 319 buf_len, (flags) | MSG_PEEK); \ 320 __recvpair(_metadata, self, \ 321 expected_buf, expected_len, buf_len, flags); \ 322 } while (0) 323 324 #define epollpair(oob_remaining) \ 325 __epollpair(_metadata, self, oob_remaining) 326 327 #define siocatmarkpair(oob_head) \ 328 __siocatmarkpair(_metadata, self, oob_head) 329 330 #define setinlinepair() \ 331 __setinlinepair(_metadata, self) 332 333 #define tcp_incompliant \ 334 for (self->tcp_compliant = false; \ 335 self->tcp_compliant == false; \ 336 self->tcp_compliant = true) 337 338 TEST_F(msg_oob, non_oob) 339 { 340 sendpair("x", 1, 0); 341 epollpair(false); 342 siocatmarkpair(false); 343 344 recvpair("", -EINVAL, 1, MSG_OOB); 345 epollpair(false); 346 siocatmarkpair(false); 347 } 348 349 TEST_F(msg_oob, oob) 350 { 351 sendpair("x", 1, MSG_OOB); 352 epollpair(true); 353 siocatmarkpair(true); 354 355 recvpair("x", 1, 1, MSG_OOB); 356 epollpair(false); 357 siocatmarkpair(true); 358 } 359 360 TEST_F(msg_oob, oob_drop) 361 { 362 sendpair("x", 1, MSG_OOB); 363 epollpair(true); 364 siocatmarkpair(true); 365 366 recvpair("", -EAGAIN, 1, 0); /* Drop OOB. */ 367 epollpair(false); 368 siocatmarkpair(false); 369 370 recvpair("", -EINVAL, 1, MSG_OOB); 371 epollpair(false); 372 siocatmarkpair(false); 373 } 374 375 TEST_F(msg_oob, oob_ahead) 376 { 377 sendpair("hello", 5, MSG_OOB); 378 epollpair(true); 379 siocatmarkpair(false); 380 381 recvpair("o", 1, 1, MSG_OOB); 382 epollpair(false); 383 siocatmarkpair(false); 384 385 recvpair("hell", 4, 4, 0); 386 epollpair(false); 387 siocatmarkpair(true); 388 } 389 390 TEST_F(msg_oob, oob_break) 391 { 392 sendpair("hello", 5, MSG_OOB); 393 epollpair(true); 394 siocatmarkpair(false); 395 396 recvpair("hell", 4, 5, 0); /* Break at OOB even with enough buffer. */ 397 epollpair(true); 398 siocatmarkpair(true); 399 400 recvpair("o", 1, 1, MSG_OOB); 401 epollpair(false); 402 siocatmarkpair(true); 403 404 recvpair("", -EAGAIN, 1, 0); 405 siocatmarkpair(false); 406 } 407 408 TEST_F(msg_oob, oob_ahead_break) 409 { 410 sendpair("hello", 5, MSG_OOB); 411 epollpair(true); 412 siocatmarkpair(false); 413 414 sendpair("world", 5, 0); 415 epollpair(true); 416 siocatmarkpair(false); 417 418 recvpair("o", 1, 1, MSG_OOB); 419 epollpair(false); 420 siocatmarkpair(false); 421 422 recvpair("hell", 4, 9, 0); /* Break at OOB even after it's recv()ed. */ 423 epollpair(false); 424 siocatmarkpair(true); 425 426 recvpair("world", 5, 5, 0); 427 epollpair(false); 428 siocatmarkpair(false); 429 } 430 431 TEST_F(msg_oob, oob_break_drop) 432 { 433 sendpair("hello", 5, MSG_OOB); 434 epollpair(true); 435 siocatmarkpair(false); 436 437 sendpair("world", 5, 0); 438 epollpair(true); 439 siocatmarkpair(false); 440 441 recvpair("hell", 4, 10, 0); /* Break at OOB even with enough buffer. */ 442 epollpair(true); 443 siocatmarkpair(true); 444 445 recvpair("world", 5, 10, 0); /* Drop OOB and recv() the next skb. */ 446 epollpair(false); 447 siocatmarkpair(false); 448 449 recvpair("", -EINVAL, 1, MSG_OOB); 450 epollpair(false); 451 siocatmarkpair(false); 452 } 453 454 TEST_F(msg_oob, ex_oob_break) 455 { 456 sendpair("hello", 5, MSG_OOB); 457 epollpair(true); 458 siocatmarkpair(false); 459 460 sendpair("wor", 3, MSG_OOB); 461 epollpair(true); 462 siocatmarkpair(false); 463 464 sendpair("ld", 2, 0); 465 epollpair(true); 466 siocatmarkpair(false); 467 468 recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */ 469 epollpair(true); 470 siocatmarkpair(true); 471 472 recvpair("r", 1, 1, MSG_OOB); 473 epollpair(false); 474 siocatmarkpair(true); 475 476 recvpair("ld", 2, 2, 0); 477 epollpair(false); 478 siocatmarkpair(false); 479 } 480 481 TEST_F(msg_oob, ex_oob_drop) 482 { 483 sendpair("x", 1, MSG_OOB); 484 epollpair(true); 485 siocatmarkpair(true); 486 487 sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */ 488 epollpair(true); 489 490 tcp_incompliant { 491 siocatmarkpair(false); 492 493 recvpair("x", 1, 1, 0); /* TCP drops "y" by passing through it. */ 494 epollpair(true); 495 siocatmarkpair(true); 496 497 recvpair("y", 1, 1, MSG_OOB); /* TCP returns -EINVAL. */ 498 epollpair(false); 499 siocatmarkpair(true); 500 } 501 } 502 503 TEST_F(msg_oob, ex_oob_drop_2) 504 { 505 sendpair("x", 1, MSG_OOB); 506 epollpair(true); 507 siocatmarkpair(true); 508 509 sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */ 510 epollpair(true); 511 512 tcp_incompliant { 513 siocatmarkpair(false); 514 } 515 516 recvpair("y", 1, 1, MSG_OOB); 517 epollpair(false); 518 519 tcp_incompliant { 520 siocatmarkpair(false); 521 522 recvpair("x", 1, 1, 0); /* TCP returns -EAGAIN. */ 523 epollpair(false); 524 siocatmarkpair(true); 525 } 526 } 527 528 TEST_F(msg_oob, ex_oob_oob) 529 { 530 sendpair("x", 1, MSG_OOB); 531 epollpair(true); 532 siocatmarkpair(true); 533 534 recvpair("x", 1, 1, MSG_OOB); 535 epollpair(false); 536 siocatmarkpair(true); 537 538 sendpair("y", 1, MSG_OOB); 539 epollpair(true); 540 siocatmarkpair(true); 541 542 recvpair("", -EAGAIN, 1, 0); 543 epollpair(false); 544 siocatmarkpair(false); 545 546 recvpair("", -EINVAL, 1, MSG_OOB); 547 epollpair(false); 548 siocatmarkpair(false); 549 } 550 551 TEST_F(msg_oob, ex_oob_ahead_break) 552 { 553 sendpair("hello", 5, MSG_OOB); 554 epollpair(true); 555 siocatmarkpair(false); 556 557 sendpair("wor", 3, MSG_OOB); 558 epollpair(true); 559 siocatmarkpair(false); 560 561 recvpair("r", 1, 1, MSG_OOB); 562 epollpair(false); 563 siocatmarkpair(false); 564 565 sendpair("ld", 2, MSG_OOB); 566 epollpair(true); 567 siocatmarkpair(false); 568 569 tcp_incompliant { 570 recvpair("hellowol", 8, 10, 0); /* TCP recv()s "helloworl", why "r" ?? */ 571 } 572 573 epollpair(true); 574 siocatmarkpair(true); 575 576 recvpair("d", 1, 1, MSG_OOB); 577 epollpair(false); 578 siocatmarkpair(true); 579 } 580 581 TEST_F(msg_oob, ex_oob_siocatmark) 582 { 583 sendpair("hello", 5, MSG_OOB); 584 epollpair(true); 585 siocatmarkpair(false); 586 587 recvpair("o", 1, 1, MSG_OOB); 588 epollpair(false); 589 siocatmarkpair(false); 590 591 sendpair("world", 5, MSG_OOB); 592 epollpair(true); 593 siocatmarkpair(false); 594 595 recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ 596 epollpair(true); 597 siocatmarkpair(false); 598 } 599 600 TEST_F(msg_oob, inline_oob) 601 { 602 setinlinepair(); 603 604 sendpair("x", 1, MSG_OOB); 605 epollpair(true); 606 siocatmarkpair(true); 607 608 recvpair("", -EINVAL, 1, MSG_OOB); 609 epollpair(true); 610 siocatmarkpair(true); 611 612 recvpair("x", 1, 1, 0); 613 epollpair(false); 614 siocatmarkpair(false); 615 } 616 617 TEST_F(msg_oob, inline_oob_break) 618 { 619 setinlinepair(); 620 621 sendpair("hello", 5, MSG_OOB); 622 epollpair(true); 623 siocatmarkpair(false); 624 625 recvpair("", -EINVAL, 1, MSG_OOB); 626 epollpair(true); 627 siocatmarkpair(false); 628 629 recvpair("hell", 4, 5, 0); /* Break at OOB but not at ex-OOB. */ 630 epollpair(true); 631 siocatmarkpair(true); 632 633 recvpair("o", 1, 1, 0); 634 epollpair(false); 635 siocatmarkpair(false); 636 } 637 638 TEST_F(msg_oob, inline_oob_ahead_break) 639 { 640 sendpair("hello", 5, MSG_OOB); 641 epollpair(true); 642 siocatmarkpair(false); 643 644 sendpair("world", 5, 0); 645 epollpair(true); 646 siocatmarkpair(false); 647 648 recvpair("o", 1, 1, MSG_OOB); 649 epollpair(false); 650 siocatmarkpair(false); 651 652 setinlinepair(); 653 654 recvpair("hell", 4, 9, 0); /* Break at OOB even with enough buffer. */ 655 epollpair(false); 656 siocatmarkpair(true); 657 658 tcp_incompliant { 659 recvpair("world", 5, 6, 0); /* TCP recv()s "oworld", ... "o" ??? */ 660 } 661 662 epollpair(false); 663 siocatmarkpair(false); 664 } 665 666 TEST_F(msg_oob, inline_ex_oob_break) 667 { 668 sendpair("hello", 5, MSG_OOB); 669 epollpair(true); 670 siocatmarkpair(false); 671 672 sendpair("wor", 3, MSG_OOB); 673 epollpair(true); 674 siocatmarkpair(false); 675 676 sendpair("ld", 2, 0); 677 epollpair(true); 678 siocatmarkpair(false); 679 680 setinlinepair(); 681 682 recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */ 683 epollpair(true); 684 siocatmarkpair(true); 685 686 recvpair("rld", 3, 3, 0); 687 epollpair(false); 688 siocatmarkpair(false); 689 } 690 691 TEST_F(msg_oob, inline_ex_oob_no_drop) 692 { 693 sendpair("x", 1, MSG_OOB); 694 epollpair(true); 695 siocatmarkpair(true); 696 697 setinlinepair(); 698 699 sendpair("y", 1, MSG_OOB); /* TCP does NOT drops "x" at this moment. */ 700 epollpair(true); 701 siocatmarkpair(false); 702 703 recvpair("x", 1, 1, 0); 704 epollpair(true); 705 siocatmarkpair(true); 706 707 recvpair("y", 1, 1, 0); 708 epollpair(false); 709 siocatmarkpair(false); 710 } 711 712 TEST_F(msg_oob, inline_ex_oob_drop) 713 { 714 sendpair("x", 1, MSG_OOB); 715 epollpair(true); 716 siocatmarkpair(true); 717 718 sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */ 719 epollpair(true); 720 721 setinlinepair(); 722 723 tcp_incompliant { 724 siocatmarkpair(false); 725 726 recvpair("x", 1, 1, 0); /* TCP recv()s "y". */ 727 epollpair(true); 728 siocatmarkpair(true); 729 730 recvpair("y", 1, 1, 0); /* TCP returns -EAGAIN. */ 731 epollpair(false); 732 siocatmarkpair(false); 733 } 734 } 735 736 TEST_F(msg_oob, inline_ex_oob_siocatmark) 737 { 738 sendpair("hello", 5, MSG_OOB); 739 epollpair(true); 740 siocatmarkpair(false); 741 742 recvpair("o", 1, 1, MSG_OOB); 743 epollpair(false); 744 siocatmarkpair(false); 745 746 setinlinepair(); 747 748 sendpair("world", 5, MSG_OOB); 749 epollpair(true); 750 siocatmarkpair(false); 751 752 recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */ 753 epollpair(true); 754 siocatmarkpair(false); 755 } 756 757 TEST_HARNESS_MAIN 758