From 7caf5b3af44c3d3b9a3b187067c59fd5fd604b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20B=C3=A9rat?= Date: Thu, 16 Oct 2025 13:05:13 +0200 Subject: [PATCH] Add new socket shutdown test (RHEL-50453) Resolves: RHEL-50453 --- glibc-RHEL-50453-1.patch | 215 +++++++++++++++++++++++++++++ glibc-RHEL-50453-2.patch | 286 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 501 insertions(+) create mode 100644 glibc-RHEL-50453-1.patch create mode 100644 glibc-RHEL-50453-2.patch diff --git a/glibc-RHEL-50453-1.patch b/glibc-RHEL-50453-1.patch new file mode 100644 index 0000000..71a394d --- /dev/null +++ b/glibc-RHEL-50453-1.patch @@ -0,0 +1,215 @@ +commit 20b6b8e8a50874dd189687df8f6f8a11d4813b34 +Author: Frédéric Bérat +Date: Wed Jun 14 10:52:06 2023 +0200 + + tests: replace read by xread + + With fortification enabled, read calls return result needs to be checked, + has it gets the __wur macro enabled. + + Note on read call removal from sysdeps/pthread/tst-cancel20.c and + sysdeps/pthread/tst-cancel21.c: + It is assumed that this second read call was there to overcome the race + condition between pipe closure and thread cancellation that could happen + in the original code. Since this race condition got fixed by + d0e3ffb7a58854248f1d5e737610d50cd0a60f46 the second call seems + superfluous. Hence, instead of checking for the return value of read, it + looks reasonable to simply remove it. + Reviewed-by: Siddhesh Poyarekar + +diff --git a/dirent/tst-fdopendir.c b/dirent/tst-fdopendir.c +index 2c9520574dddfba8..d6a24f47db25b7b6 100644 +--- a/dirent/tst-fdopendir.c ++++ b/dirent/tst-fdopendir.c +@@ -45,7 +45,8 @@ do_test (void) + } + + char buf[5]; +- read(fd, buf, sizeof (buf)); ++ xread(fd, buf, sizeof (buf)); ++ + close(fd); + + struct stat64 st2; +diff --git a/nptl/tst-cleanup4.c b/nptl/tst-cleanup4.c +index 4a275ed950d6c5f8..8c95d944f9440360 100644 +--- a/nptl/tst-cleanup4.c ++++ b/nptl/tst-cleanup4.c +@@ -22,6 +22,8 @@ + #include + #include + ++#include ++ + /* LinuxThreads pthread_cleanup_{push,pop} helpers. */ + extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer, + void (*__routine) (void *), +@@ -65,7 +67,7 @@ fn_read (void) + } + + char c; +- read (fds[0], &c, 1); ++ xread (fds[0], &c, 1); + } + + +diff --git a/support/Makefile b/support/Makefile +index fa3abb39d587a501..e89eeec45eba14a9 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -210,6 +210,7 @@ libsupport-routines = \ + xpthread_spin_lock \ + xpthread_spin_unlock \ + xraise \ ++ xread \ + xreadlink \ + xrealloc \ + xrecvfrom \ +diff --git a/support/test-container.c b/support/test-container.c +index 9a9d4b9ac6180c88..df8f74801f5ed641 100644 +--- a/support/test-container.c ++++ b/support/test-container.c +@@ -1209,7 +1209,8 @@ main (int argc, char **argv) + + /* Get our "outside" pid from our parent. We use this to help with + debugging from outside the container. */ +- read (pipes[0], &child, sizeof(child)); ++ xread (pipes[0], &child, sizeof(child)); ++ + close (pipes[0]); + close (pipes[1]); + sprintf (pid_buf, "%lu", (long unsigned)child); +diff --git a/support/xread.c b/support/xread.c +new file mode 100644 +index 0000000000000000..19f76fcfeb7165d6 +--- /dev/null ++++ b/support/xread.c +@@ -0,0 +1,36 @@ ++/* read with error checking and retries. ++ Copyright (C) 2023 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++ ++#include ++ ++void ++xread (int fd, void *buffer, size_t length) ++{ ++ char *p = buffer; ++ char *end = p + length; ++ while (p < end) ++ { ++ ssize_t ret = read (fd, p, end - p); ++ if (ret < 0) ++ FAIL_EXIT1 ("read of %zu bytes failed after %td: %m", ++ length, p - (char *) buffer); ++ p += ret; ++ } ++} +diff --git a/support/xunistd.h b/support/xunistd.h +index a4d0d3427204cfe5..68feedef7fc3831d 100644 +--- a/support/xunistd.h ++++ b/support/xunistd.h +@@ -78,6 +78,9 @@ void xclose (int); + /* Write the buffer. Retry on short writes. */ + void xwrite (int, const void *, size_t); + ++/* Read to buffer. Retry on short reads. */ ++void xread (int, void *, size_t); ++ + /* Invoke mmap with a zero file offset. */ + void *xmmap (void *addr, size_t length, int prot, int flags, int fd); + void xmprotect (void *addr, size_t length, int prot); +diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile +index fa70a33e25f23230..e9c54214da1ffb82 100644 +--- a/sysdeps/pthread/Makefile ++++ b/sysdeps/pthread/Makefile +@@ -281,7 +281,7 @@ $(objpfx)tst-cancel28: $(librt) + + $(objpfx)tst-fini1: $(shared-thread-library) $(objpfx)tst-fini1mod.so + +-$(objpfx)tst-fini1mod.so: $(shared-thread-library) ++$(objpfx)tst-fini1mod.so: $(libsupport) $(shared-thread-library) + + $(objpfx)tst-_res1mod2.so: $(objpfx)tst-_res1mod1.so + LDFLAGS-tst-_res1mod1.so = -Wl,-soname,tst-_res1mod1.so +diff --git a/sysdeps/pthread/tst-cancel11.c b/sysdeps/pthread/tst-cancel11.c +index e485e0827e1895e5..a76638b3b629850a 100644 +--- a/sysdeps/pthread/tst-cancel11.c ++++ b/sysdeps/pthread/tst-cancel11.c +@@ -57,7 +57,11 @@ tf (void *arg) + + /* This call should block and be cancelable. */ + char buf[20]; +- read (fd[0], buf, sizeof (buf)); ++ if (read (fd[0], buf, sizeof (buf))) ++ { ++ puts ("read unexpectedly returned"); ++ exit (1); ++ } + + pthread_cleanup_pop (0); + +diff --git a/sysdeps/pthread/tst-cancel20.c b/sysdeps/pthread/tst-cancel20.c +index 194726a0d4c56d53..0bed320bd3785685 100644 +--- a/sysdeps/pthread/tst-cancel20.c ++++ b/sysdeps/pthread/tst-cancel20.c +@@ -85,8 +85,6 @@ tf_body (void) + exit (1); + } + +- read (fd[0], &c, 1); +- + pthread_cleanup_pop (0); + } + +diff --git a/sysdeps/pthread/tst-cancel21.c b/sysdeps/pthread/tst-cancel21.c +index 2fe248082ec94b25..bfce7cbb98e53343 100644 +--- a/sysdeps/pthread/tst-cancel21.c ++++ b/sysdeps/pthread/tst-cancel21.c +@@ -86,8 +86,6 @@ tf_body (void) + exit (1); + } + +- read (fd[0], &c, 1); +- + pthread_cleanup_pop (0); + } + +diff --git a/sysdeps/pthread/tst-fini1mod.c b/sysdeps/pthread/tst-fini1mod.c +index cc6b9ee98220d63d..b40f9d5c7ba88caa 100644 +--- a/sysdeps/pthread/tst-fini1mod.c ++++ b/sysdeps/pthread/tst-fini1mod.c +@@ -21,6 +21,8 @@ + #include + #include + ++#include ++ + + static void * + tf (void *arg) +@@ -33,7 +35,7 @@ tf (void *arg) + } + + char buf[10]; +- read (fds[0], buf, sizeof (buf)); ++ xread (fds[0], buf, sizeof (buf)); + + puts ("read returned"); + exit (1); diff --git a/glibc-RHEL-50453-2.patch b/glibc-RHEL-50453-2.patch new file mode 100644 index 0000000..8250015 --- /dev/null +++ b/glibc-RHEL-50453-2.patch @@ -0,0 +1,286 @@ +commit 50f5a09e68e0c4ea60f5dfee3cc6963df2762e14 +Author: Sergey Kolosov +Date: Tue Jun 25 11:19:03 2024 +0200 + + socket: Add new test for shutdown + + This commit adds shutdown test with SHUT_RD, SHUT_WR, SHUT_RDWR for an + UNIX socket connection. + + Reviewed-by: DJ Delorie + +diff --git a/socket/Makefile b/socket/Makefile +index a74e1f66ce7e5f6e..ca089211687515a9 100644 +--- a/socket/Makefile ++++ b/socket/Makefile +@@ -35,6 +35,7 @@ tests := \ + tst-accept4 \ + tst-cmsghdr \ + tst-connect \ ++ tst-shutdown \ + tst-sockopt \ + # tests + +diff --git a/socket/tst-shutdown.c b/socket/tst-shutdown.c +new file mode 100644 +index 0000000000000000..a305e5e494cc38f1 +--- /dev/null ++++ b/socket/tst-shutdown.c +@@ -0,0 +1,257 @@ ++/* Test the shutdown function. ++ Copyright (C) 2024 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct connection ++{ ++ int sockets[2]; ++}; ++ ++void ++establish_connection (struct connection *conn) ++{ ++ if (socketpair (AF_UNIX, SOCK_STREAM, 0, conn->sockets) != 0) ++ { ++ FAIL_EXIT1 ("socketpair (AF_UNIX, SOCK_STREAM, 0): %m\n"); ++ } ++} ++ ++void ++close_connection (struct connection *conn) ++{ ++ xclose (conn->sockets[0]); ++ xclose (conn->sockets[1]); ++} ++ ++/* Open a file and check that shutdown fails with the ENOTSOCK error code. */ ++void ++do_test_enotsock (void) ++{ ++ /* Open file and check that shutdown will fail with ENOTSOCK. */ ++ int fd = xopen ("/dev/null", O_RDWR, 0); ++ ++ int result = shutdown (fd, SHUT_RD); ++ if (result == 0 || errno != ENOTSOCK) ++ { ++ FAIL_EXIT1 ("shutdown should fail with ENOTSOCK"); ++ } ++ xclose (fd); ++} ++ ++/* Test shutdown with SHUT_RD. */ ++void ++do_test_shut_rd (void) ++{ ++ struct connection conn; ++ const char *str = "AAAAAAA"; ++ int len = 8; ++ int ret; ++ void *s_buf = xmalloc (len); ++ bzero (s_buf, len); ++ ++ establish_connection (&conn); ++ int server = conn.sockets[0]; ++ int client = conn.sockets[1]; ++ ++ /* Call shutdown with SHUT_RD on server socket. */ ++ if (shutdown (server, SHUT_RD) != 0) ++ { ++ FAIL_EXIT1 ("shutdown with SHUT_RD on socket %d failed", server); ++ } ++ ++ ret = send (server, str, len, 0); ++ if (ret <= 0) ++ { ++ FAIL_EXIT1 ("send (%d, data, %d): %m", server, len); ++ } ++ ++ ret = recv (client, s_buf, len, 0); ++ if (ret <= 0) ++ { ++ FAIL_EXIT1 ("recv (%d, data, %d): %m", client, len); ++ } ++ ++ TEST_COMPARE_BLOB (str, len, s_buf, len); ++ ++ /* Send data should be disallowed on shutdown socket. */ ++ errno = 0; ++ ret = send (client, str, len, MSG_NOSIGNAL); ++ if (ret >= 0 || errno != EPIPE) ++ { ++ FAIL_EXIT1 ("Send on SHUT_RD socket should be disallowed: %m"); ++ } ++ ++ /* Recv should return zero and no error. */ ++ errno = 0; ++ ret = recv (server, s_buf, len, 0); ++ if (ret != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("recv should return 0 without error: %m"); ++ } ++ ++ close_connection (&conn); ++} ++ ++/* Test shutdown with SHUT_WR. */ ++void ++do_test_shut_wr (void) ++{ ++ struct connection conn; ++ const char *str1 = "CCCCCCC"; ++ const char *str2 = "DDDDDDD"; ++ const char *str3 = "EEEEEEE"; ++ int len = 8; ++ int ret; ++ void *c_buf = xmalloc (len); ++ void *s_buf = xmalloc (len); ++ ++ establish_connection (&conn); ++ int server = conn.sockets[0]; ++ int client = conn.sockets[1]; ++ ++ xwrite (client, str1, len); ++ ++ if (shutdown (client, SHUT_WR) != 0) ++ { ++ FAIL_EXIT1 ("shutdown with SHUT_WR on socket %d failed", client); ++ } ++ ++ ret = send (client, str2, len, MSG_NOSIGNAL); ++ if (ret >= 0) ++ { ++ FAIL_EXIT1 ("send on SHUT_WR socket should fail"); ++ } ++ ++ /* Read data written before shutdown and check if it's correct. */ ++ xread (server, s_buf, len); ++ TEST_COMPARE_BLOB (str1, len, s_buf, len); ++ ++ /* Second read should return zero without error. */ ++ errno = 0; ++ if (read (server, s_buf, len) != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("read after shutdown should return zero without error: %m"); ++ } ++ ++ /* Write some data to socket and check it still can be read on other side. */ ++ memcpy (s_buf, str3, len); ++ xwrite (server, s_buf, len); ++ ++ xread (client, c_buf, len); ++ TEST_COMPARE_BLOB (s_buf, len, c_buf, len); ++ ++ close_connection (&conn); ++} ++ ++/* Test shutdown with SHUT_RDWR. */ ++void ++do_test_shut_rdwr (void) ++{ ++ struct connection conn; ++ struct sockaddr peer; ++ socklen_t peer_len = sizeof (peer); ++ ++ const char *str1 = "FFFFFFF"; ++ const char *str2 = "GGGGGGG"; ++ int len = 8; ++ int ret; ++ void *s_buf = xmalloc (len); ++ bzero (s_buf, len); ++ ++ establish_connection (&conn); ++ int server = conn.sockets[0]; ++ int client = conn.sockets[1]; ++ ++ /* Send some data to both sockets before shutdown. */ ++ xwrite (client, str1, len); ++ xwrite (server, str2, len); ++ ++ /* Call shutdown with SHUT_RDWR on client socket. */ ++ if (shutdown (client, SHUT_RDWR) != 0) ++ { ++ FAIL_EXIT1 ("shutdown with SHUT_RDWR on socket %d failed", client); ++ } ++ ++ /* Verify that socket is still connected. */ ++ xgetsockname (client, &peer, &peer_len); ++ ++ /* Read data written before shutdown. */ ++ xread (client, s_buf, len); ++ TEST_COMPARE_BLOB (s_buf, len, str2, len); ++ ++ /* Second read should return zero, but no error. */ ++ errno = 0; ++ if (read (client, s_buf, len) != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("read after shutdown should return zero without error: %m"); ++ } ++ ++ /* Send some data to shutdown socket and expect error. */ ++ errno = 0; ++ ret = send (server, str2, len, MSG_NOSIGNAL); ++ if (ret >= 0 || errno != EPIPE) ++ { ++ FAIL_EXIT1 ("send to RDWR shutdown socket should fail with EPIPE"); ++ } ++ ++ /* Read data written before shutdown. */ ++ xread (server, s_buf, len); ++ TEST_COMPARE_BLOB (s_buf, len, str1, len); ++ ++ /* Second read should return zero, but no error. */ ++ errno = 0; ++ if (read (server, s_buf, len) != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("read after shutdown should return zero without error: %m"); ++ } ++ ++ /* Send some data to shutdown socket and expect error. */ ++ errno = 0; ++ ret = send (client, str1, len, MSG_NOSIGNAL); ++ if (ret >= 0 || errno != EPIPE) ++ { ++ FAIL_EXIT1 ("send to RDWR shutdown socket should fail with EPIPE"); ++ } ++ ++ close_connection (&conn); ++} ++ ++static int ++do_test (void) ++{ ++ do_test_enotsock (); ++ do_test_shut_rd (); ++ do_test_shut_wr (); ++ do_test_shut_rdwr (); ++ ++ return 0; ++} ++ ++#include