* [PATCH 1/4] selftests/core: fix close_range_test build after XFAIL removal
[not found] <https://lore.kernel.org/linux-fsdevel/20201217213303.722643-1-christian.brauner@ubuntu.com>
@ 2020-12-18 14:54 ` Christian Brauner
2020-12-18 14:54 ` [PATCH 2/4] selftests/core: handle missing syscall number for close_range Christian Brauner
` (2 subsequent siblings)
3 siblings, 0 replies; 4+ messages in thread
From: Christian Brauner @ 2020-12-18 14:54 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Giuseppe Scrivano, Tobias Klauser, Christian Brauner
From: Tobias Klauser <tklauser@distanz.ch>
XFAIL was removed in commit 9847d24af95c ("selftests/harness: Refactor
XFAIL into SKIP") and its use in close_range_test was already replaced
by commit 1d44d0dd61b6 ("selftests: core: use SKIP instead of XFAIL in
close_range_test.c"). However, commit 23afeaeff3d9 ("selftests: core:
add tests for CLOSE_RANGE_CLOEXEC") introduced usage of XFAIL in
TEST(close_range_cloexec). Use SKIP there as well.
Cc: Giuseppe Scrivano <gscrivan@redhat.com>
Fixes: 23afeaeff3d9 ("selftests: core: add tests for CLOSE_RANGE_CLOEXEC")
Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
Link: https://lore.kernel.org/r/20201218112428.13662-1-tklauser@distanz.ch
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
tools/testing/selftests/core/close_range_test.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/core/close_range_test.c b/tools/testing/selftests/core/close_range_test.c
index 9625d2f2188a..c97dd1a7abd6 100644
--- a/tools/testing/selftests/core/close_range_test.c
+++ b/tools/testing/selftests/core/close_range_test.c
@@ -241,7 +241,7 @@ TEST(close_range_cloexec)
fd = open("/dev/null", O_RDONLY);
ASSERT_GE(fd, 0) {
if (errno == ENOENT)
- XFAIL(return, "Skipping test since /dev/null does not exist");
+ SKIP(return, "Skipping test since /dev/null does not exist");
}
open_fds[i] = fd;
@@ -250,9 +250,9 @@ TEST(close_range_cloexec)
ret = sys_close_range(1000, 1000, CLOSE_RANGE_CLOEXEC);
if (ret < 0) {
if (errno == ENOSYS)
- XFAIL(return, "close_range() syscall not supported");
+ SKIP(return, "close_range() syscall not supported");
if (errno == EINVAL)
- XFAIL(return, "close_range() doesn't support CLOSE_RANGE_CLOEXEC");
+ SKIP(return, "close_range() doesn't support CLOSE_RANGE_CLOEXEC");
}
/* Ensure the FD_CLOEXEC bit is set also with a resource limit in place. */
--
2.29.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/4] selftests/core: handle missing syscall number for close_range
[not found] <https://lore.kernel.org/linux-fsdevel/20201217213303.722643-1-christian.brauner@ubuntu.com>
2020-12-18 14:54 ` [PATCH 1/4] selftests/core: fix close_range_test build after XFAIL removal Christian Brauner
@ 2020-12-18 14:54 ` Christian Brauner
2020-12-18 14:54 ` [PATCH 3/4] selftests/core: add test for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC Christian Brauner
2020-12-18 14:54 ` [PATCH 4/4] selftests/core: add regression " Christian Brauner
3 siblings, 0 replies; 4+ messages in thread
From: Christian Brauner @ 2020-12-18 14:54 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Giuseppe Scrivano, Christian Brauner
This improves the syscall number handling in the close_range()
selftests. This should handle any architecture.
Cc: Giuseppe Scrivano <gscrivan@redhat.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
.../testing/selftests/core/close_range_test.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/core/close_range_test.c b/tools/testing/selftests/core/close_range_test.c
index c97dd1a7abd6..bc592a1372bb 100644
--- a/tools/testing/selftests/core/close_range_test.c
+++ b/tools/testing/selftests/core/close_range_test.c
@@ -17,7 +17,23 @@
#include "../clone3/clone3_selftests.h"
#ifndef __NR_close_range
-#define __NR_close_range -1
+ #if defined __alpha__
+ #define __NR_close_range 546
+ #elif defined _MIPS_SIM
+ #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
+ #define __NR_close_range (436 + 4000)
+ #endif
+ #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
+ #define __NR_close_range (436 + 6000)
+ #endif
+ #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
+ #define __NR_close_range (436 + 5000)
+ #endif
+ #elif defined __ia64__
+ #define __NR_close_range (436 + 1024)
+ #else
+ #define __NR_close_range 436
+ #endif
#endif
#ifndef CLOSE_RANGE_UNSHARE
--
2.29.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/4] selftests/core: add test for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC
[not found] <https://lore.kernel.org/linux-fsdevel/20201217213303.722643-1-christian.brauner@ubuntu.com>
2020-12-18 14:54 ` [PATCH 1/4] selftests/core: fix close_range_test build after XFAIL removal Christian Brauner
2020-12-18 14:54 ` [PATCH 2/4] selftests/core: handle missing syscall number for close_range Christian Brauner
@ 2020-12-18 14:54 ` Christian Brauner
2020-12-18 14:54 ` [PATCH 4/4] selftests/core: add regression " Christian Brauner
3 siblings, 0 replies; 4+ messages in thread
From: Christian Brauner @ 2020-12-18 14:54 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Giuseppe Scrivano, Christian Brauner
Add a test to verify that CLOSE_RANGE_UNSHARE works correctly when combined
with CLOSE_RANGE_CLOEXEC for the single-threaded case.
Cc: Giuseppe Scrivano <gscrivan@redhat.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
.../testing/selftests/core/close_range_test.c | 70 +++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/tools/testing/selftests/core/close_range_test.c b/tools/testing/selftests/core/close_range_test.c
index bc592a1372bb..862444f1c244 100644
--- a/tools/testing/selftests/core/close_range_test.c
+++ b/tools/testing/selftests/core/close_range_test.c
@@ -313,5 +313,75 @@ TEST(close_range_cloexec)
}
}
+TEST(close_range_cloexec_unshare)
+{
+ int i, ret;
+ int open_fds[101];
+ struct rlimit rlimit;
+
+ for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
+ int fd;
+
+ fd = open("/dev/null", O_RDONLY);
+ ASSERT_GE(fd, 0) {
+ if (errno == ENOENT)
+ SKIP(return, "Skipping test since /dev/null does not exist");
+ }
+
+ open_fds[i] = fd;
+ }
+
+ ret = sys_close_range(1000, 1000, CLOSE_RANGE_CLOEXEC);
+ if (ret < 0) {
+ if (errno == ENOSYS)
+ SKIP(return, "close_range() syscall not supported");
+ if (errno == EINVAL)
+ SKIP(return, "close_range() doesn't support CLOSE_RANGE_CLOEXEC");
+ }
+
+ /* Ensure the FD_CLOEXEC bit is set also with a resource limit in place. */
+ ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit));
+ rlimit.rlim_cur = 25;
+ ASSERT_EQ(0, setrlimit(RLIMIT_NOFILE, &rlimit));
+
+ /* Set close-on-exec for two ranges: [0-50] and [75-100]. */
+ ret = sys_close_range(open_fds[0], open_fds[50],
+ CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
+ ASSERT_EQ(0, ret);
+ ret = sys_close_range(open_fds[75], open_fds[100],
+ CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
+ ASSERT_EQ(0, ret);
+
+ for (i = 0; i <= 50; i++) {
+ int flags = fcntl(open_fds[i], F_GETFD);
+
+ EXPECT_GT(flags, -1);
+ EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
+ }
+
+ for (i = 51; i <= 74; i++) {
+ int flags = fcntl(open_fds[i], F_GETFD);
+
+ EXPECT_GT(flags, -1);
+ EXPECT_EQ(flags & FD_CLOEXEC, 0);
+ }
+
+ for (i = 75; i <= 100; i++) {
+ int flags = fcntl(open_fds[i], F_GETFD);
+
+ EXPECT_GT(flags, -1);
+ EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
+ }
+
+ /* Test a common pattern. */
+ ret = sys_close_range(3, UINT_MAX,
+ CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
+ for (i = 0; i <= 100; i++) {
+ int flags = fcntl(open_fds[i], F_GETFD);
+
+ EXPECT_GT(flags, -1);
+ EXPECT_EQ(flags & FD_CLOEXEC, FD_CLOEXEC);
+ }
+}
TEST_HARNESS_MAIN
--
2.29.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 4/4] selftests/core: add regression test for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC
[not found] <https://lore.kernel.org/linux-fsdevel/20201217213303.722643-1-christian.brauner@ubuntu.com>
` (2 preceding siblings ...)
2020-12-18 14:54 ` [PATCH 3/4] selftests/core: add test for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC Christian Brauner
@ 2020-12-18 14:54 ` Christian Brauner
3 siblings, 0 replies; 4+ messages in thread
From: Christian Brauner @ 2020-12-18 14:54 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Giuseppe Scrivano, Christian Brauner
This test is a minimalized version of the reproducer given by syzbot
(cf. [1]).
After introducing CLOSE_RANGE_CLOEXEC syzbot reported a crash when
CLOSE_RANGE_CLOEXEC is specified in conjunction with
CLOSE_RANGE_UNSHARE. When CLOSE_RANGE_UNSHARE is specified the caller
will receive a private file descriptor table in case their file
descriptor table is currently shared. When the caller requests that all
file descriptors are supposed to be operated on via e.g. a call like
close_range(3, ~0U) and the caller shares their file descriptor table
then the kernel will only copy all files in the range from 0 to 3 and no
others.
The original bug used the maximum of the old file descriptor table not
the new one. In order to test this bug we need to first create a huge
large gap in the fd table. When we now call CLOSE_RANGE_UNSHARE with a
shared fd table and and with ~0U as upper bound the kernel will only
copy up to fd1 file descriptors into the new fd table. If max_fd in the
close_range() codepaths isn't correctly set when requesting
CLOSE_RANGE_CLOEXEC with all of these fds we will see NULL pointer
derefs!
This test passes on a fixed kernel.
Cc: Giuseppe Scrivano <gscrivan@redhat.com>
[1]: https://syzkaller.appspot.com/text?tag=KernelConfig&x=db720fe37a6a41d8
Link: syzbot+96cfd2b22b3213646a93@syzkaller.appspotmail.com
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
---
tools/testing/selftests/core/Makefile | 2 +-
.../testing/selftests/core/close_range_test.c | 231 +++++++++++++++++-
2 files changed, 230 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/core/Makefile b/tools/testing/selftests/core/Makefile
index f6f2d6f473c6..5ceb3ba1ca9c 100644
--- a/tools/testing/selftests/core/Makefile
+++ b/tools/testing/selftests/core/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -g -I../../../../usr/include/
+CFLAGS += -g -I../../../../usr/include/ -pthread
TEST_GEN_PROGS := close_range_test
diff --git a/tools/testing/selftests/core/close_range_test.c b/tools/testing/selftests/core/close_range_test.c
index 862444f1c244..65f071d8fd16 100644
--- a/tools/testing/selftests/core/close_range_test.c
+++ b/tools/testing/selftests/core/close_range_test.c
@@ -3,15 +3,22 @@
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
-#include <linux/kernel.h>
#include <limits.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <syscall.h>
#include <unistd.h>
-#include <sys/resource.h>
#include "../kselftest_harness.h"
#include "../clone3/clone3_selftests.h"
@@ -384,4 +391,224 @@ TEST(close_range_cloexec_unshare)
}
}
+static uint64_t current_time_ms(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ exit(EXIT_FAILURE);
+
+ return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
+}
+
+static void thread_start(void *(*fn)(void *), void *arg)
+{
+ int i;
+ pthread_t th;
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 128 << 10);
+
+ for (i = 0; i < 100; i++) {
+ if (pthread_create(&th, &attr, fn, arg) == 0) {
+ pthread_attr_destroy(&attr);
+ return;
+ }
+
+ if (errno == EAGAIN) {
+ usleep(50);
+ continue;
+ }
+
+ break;
+ }
+
+ exit(EXIT_FAILURE);
+}
+
+static void event_init(int *state)
+{
+ *state = 0;
+}
+
+static void event_reset(int *state)
+{
+ *state = 0;
+}
+
+static void event_set(int *state)
+{
+ if (*state)
+ exit(EXIT_FAILURE);
+
+ __atomic_store_n(state, 1, __ATOMIC_RELEASE);
+ syscall(SYS_futex, state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000);
+}
+
+static void event_wait(int *state)
+{
+ while (!__atomic_load_n(state, __ATOMIC_ACQUIRE))
+ syscall(SYS_futex, state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0);
+}
+
+static int event_isset(int *state)
+{
+ return __atomic_load_n(state, __ATOMIC_ACQUIRE);
+}
+
+static int event_timedwait(int *state, uint64_t timeout)
+{
+ uint64_t start = current_time_ms();
+ uint64_t now = start;
+ for (;;) {
+ struct timespec ts;
+ uint64_t remain = timeout - (now - start);
+
+ ts.tv_sec = remain / 1000;
+ ts.tv_nsec = (remain % 1000) * 1000 * 1000;
+
+ syscall(SYS_futex, state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts);
+
+ if (__atomic_load_n(state, __ATOMIC_ACQUIRE))
+ return 1;
+
+ now = current_time_ms();
+ if (now - start > timeout)
+ return 0;
+ }
+}
+
+struct thread_t {
+ int created;
+ int call;
+ int ready;
+ int done;
+};
+
+static struct thread_t threads[4];
+static int running;
+
+static void thread_close_range_call(int call)
+{
+ int fd = 0;
+
+ switch (call) {
+ case 0:
+ fd = openat(-1, "/dev/null", 0, 0);
+ if (fd < 0)
+ fd = 0;
+ break;
+ case 1:
+ sys_close_range(fd, -1, CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC);
+ break;
+ }
+}
+
+static void *thread_close_range(void *arg)
+{
+ struct thread_t *th = (struct thread_t *)arg;
+ for (;;) {
+ event_wait(&th->ready);
+ event_reset(&th->ready);
+ thread_close_range_call(th->call);
+ __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED);
+ event_set(&th->done);
+ }
+ return 0;
+}
+
+static void threaded_close_range(void)
+{
+ int i, fd, call, thread;
+ for (call = 0; call < 2; call++) {
+ for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); thread++) {
+ struct thread_t *th = &threads[thread];
+ if (!th->created) {
+ th->created = 1;
+ event_init(&th->ready);
+ event_init(&th->done);
+ event_set(&th->done);
+ thread_start(thread_close_range, th);
+ }
+
+ if (!event_isset(&th->done))
+ continue;
+
+ event_reset(&th->done);
+ th->call = call;
+ __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED);
+ event_set(&th->ready);
+ event_timedwait(&th->done, 45);
+ break;
+ }
+ }
+
+ for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++)
+ usleep(1000);
+
+ for (fd = 3; fd < 30; fd++)
+ close(fd);
+}
+
+/*
+ * Regression test for syzbot+96cfd2b22b3213646a93@syzkaller.appspotmail.com
+ */
+TEST(close_range_cloexec_unshare_threaded_syzbot)
+{
+ int iter;
+ int fd1, fd2, fd3;
+
+ /*
+ * Create a huge gap in the fd table. When we now call
+ * CLOSE_RANGE_UNSHARE with a shared fd table and and with ~0U as upper
+ * bound the kernel will only copy up to fd1 file descriptors into the
+ * new fd table. If max_fd in the close_range() codepaths isn't
+ * correctly set when requesting CLOSE_RANGE_CLOEXEC with all of these
+ * fds we will see NULL pointer derefs!
+ */
+ fd1 = open("/dev/null", O_RDWR);
+ EXPECT_GT(fd1, 0);
+
+ fd3 = dup2(fd1, 1000);
+ EXPECT_GT(fd3, 0);
+
+ for (iter = 0; iter <= 1000; iter++) {
+ pid_t pid;
+ int status;
+ uint64_t start;
+
+ pid = fork();
+ if (pid < 0)
+ exit(EXIT_FAILURE);
+ if (pid == 0) {
+ EXPECT_EQ(prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0), 0);
+ setpgrp();
+
+ threaded_close_range();
+ exit(EXIT_SUCCESS);
+ }
+
+ status = 0;
+ start = current_time_ms();
+ for (;;) {
+ if (waitpid(-1, &status, WNOHANG | __WALL) == pid)
+ break;
+
+ usleep(1000);
+
+ if (current_time_ms() - start < 5 * 1000)
+ continue;
+
+ kill(pid, SIGKILL);
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+
+ EXPECT_EQ(true, WIFEXITED(status));
+
+ EXPECT_EQ(0, WEXITSTATUS(status));
+ }
+ }
+}
+
TEST_HARNESS_MAIN
--
2.29.2
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2020-12-18 14:55 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <https://lore.kernel.org/linux-fsdevel/20201217213303.722643-1-christian.brauner@ubuntu.com>
2020-12-18 14:54 ` [PATCH 1/4] selftests/core: fix close_range_test build after XFAIL removal Christian Brauner
2020-12-18 14:54 ` [PATCH 2/4] selftests/core: handle missing syscall number for close_range Christian Brauner
2020-12-18 14:54 ` [PATCH 3/4] selftests/core: add test for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC Christian Brauner
2020-12-18 14:54 ` [PATCH 4/4] selftests/core: add regression " Christian Brauner
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.