Add new socket shutdown test (RHEL-50453)

Resolves: RHEL-50453
This commit is contained in:
Frédéric Bérat 2025-10-16 13:05:13 +02:00
parent b4d304b208
commit 7caf5b3af4
2 changed files with 501 additions and 0 deletions

215
glibc-RHEL-50453-1.patch Normal file
View File

@ -0,0 +1,215 @@
commit 20b6b8e8a50874dd189687df8f6f8a11d4813b34
Author: Frédéric Bérat <fberat@redhat.com>
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 <siddhesh@sourceware.org>
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 <stdlib.h>
#include <unistd.h>
+#include <support/xunistd.h>
+
/* 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <support/xunistd.h>
+
+#include <support/check.h>
+
+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 <stdlib.h>
#include <unistd.h>
+#include <support/xunistd.h>
+
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);

286
glibc-RHEL-50453-2.patch Normal file
View File

@ -0,0 +1,286 @@
commit 50f5a09e68e0c4ea60f5dfee3cc6963df2762e14
Author: Sergey Kolosov <skolosov@redhat.com>
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 <dj@redhat.com>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xsocket.h>
+#include <support/xunistd.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+
+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 <support/test-driver.c>