@@ -1479,7 +1479,7 @@ static int copy_files(unsigned long clone_flags, struct task_struct *tsk)
goto out;
}

newf = dup_fd(oldf, &error);
newf = dup_fd(oldf, NR_OPEN_MAX, &error);
if (!newf)
goto out;

@@ -2866,14 +2866,15 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
/*
* Unshare file descriptor table if it is being shared
*/
static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
int unshare_fd(unsigned long unshare_flags, unsigned int max_fds,
struct files_struct **new_fdp)
{
struct files_struct *fd = current->files;
int error = 0;

if ((unshare_flags & CLONE_FILES) &&
(fd && atomic_read(&fd->count) > 1)) {
*new_fdp = dup_fd(fd, &error);
*new_fdp = dup_fd(fd, max_fds, &error);
if (!*new_fdp)
return error;
}
@@ -2933,7 +2934,7 @@ int ksys_unshare(unsigned long unshare_flags)
err = unshare_fs(unshare_flags, &new_fs);
if (err)
goto bad_unshare_out;
err = unshare_fd(unshare_flags, &new_fd);
err = unshare_fd(unshare_flags, NR_OPEN_MAX, &new_fd);
if (err)
goto bad_unshare_cleanup_fs;
err = unshare_userns(unshare_flags, &new_cred);
@@ -3022,7 +3023,7 @@ int unshare_files(struct files_struct **displaced)
struct files_struct *copy = NULL;
int error;

error = unshare_fd(CLONE_FILES, &copy);
error = unshare_fd(CLONE_FILES, NR_OPEN_MAX, &copy);
if (error || !copy) {
*displaced = NULL;
return error;
@@ -6,6 +6,7 @@ TARGETS += breakpoints
TARGETS += capabilities
TARGETS += cgroup
TARGETS += clone3
TARGETS += core
TARGETS += cpufreq
TARGETS += cpu-hotplug
TARGETS += drivers/dma-buf
@@ -0,0 +1 @@
close_range_test
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -g -I../../../../usr/include/

TEST_GEN_PROGS := close_range_test

include ../lib.mk

@@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/kernel.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
#include <unistd.h>

#include "../kselftest_harness.h"
#include "../clone3/clone3_selftests.h"

#ifndef __NR_close_range
#define __NR_close_range -1
#endif

#ifndef CLOSE_RANGE_UNSHARE
#define CLOSE_RANGE_UNSHARE (1U << 1)
#endif

static inline int sys_close_range(unsigned int fd, unsigned int max_fd,
unsigned int flags)
{
return syscall(__NR_close_range, fd, max_fd, flags);
}

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif

TEST(close_range)
{
int i, ret;
int open_fds[101];

for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
int fd;

fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
ASSERT_GE(fd, 0) {
if (errno == ENOENT)
XFAIL(return, "Skipping test since /dev/null does not exist");
}

open_fds[i] = fd;
}

EXPECT_EQ(-1, sys_close_range(open_fds[0], open_fds[100], -1)) {
if (errno == ENOSYS)
XFAIL(return, "close_range() syscall not supported");
}

EXPECT_EQ(0, sys_close_range(open_fds[0], open_fds[50], 0));

for (i = 0; i <= 50; i++)
EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));

for (i = 51; i <= 100; i++)
EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);

/* create a couple of gaps */
close(57);
close(78);
close(81);
close(82);
close(84);
close(90);

EXPECT_EQ(0, sys_close_range(open_fds[51], open_fds[92], 0));

for (i = 51; i <= 92; i++)
EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));

for (i = 93; i <= 100; i++)
EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);

/* test that the kernel caps and still closes all fds */
EXPECT_EQ(0, sys_close_range(open_fds[93], open_fds[99], 0));

for (i = 93; i <= 99; i++)
EXPECT_EQ(-1, fcntl(open_fds[i], F_GETFL));

EXPECT_GT(fcntl(open_fds[i], F_GETFL), -1);

EXPECT_EQ(0, sys_close_range(open_fds[100], open_fds[100], 0));

EXPECT_EQ(-1, fcntl(open_fds[100], F_GETFL));
}

TEST(close_range_unshare)
{
int i, ret, status;
pid_t pid;
int open_fds[101];
struct clone_args args = {
.flags = CLONE_FILES,
.exit_signal = SIGCHLD,
};

for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
int fd;

fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
ASSERT_GE(fd, 0) {
if (errno == ENOENT)
XFAIL(return, "Skipping test since /dev/null does not exist");
}

open_fds[i] = fd;
}

pid = sys_clone3(&args, sizeof(args));
ASSERT_GE(pid, 0);

if (pid == 0) {
ret = sys_close_range(open_fds[0], open_fds[50],
CLOSE_RANGE_UNSHARE);
if (ret)
exit(EXIT_FAILURE);

for (i = 0; i <= 50; i++)
if (fcntl(open_fds[i], F_GETFL) != -1)
exit(EXIT_FAILURE);

for (i = 51; i <= 100; i++)
if (fcntl(open_fds[i], F_GETFL) == -1)
exit(EXIT_FAILURE);

/* create a couple of gaps */
close(57);
close(78);
close(81);
close(82);
close(84);
close(90);

ret = sys_close_range(open_fds[51], open_fds[92],
CLOSE_RANGE_UNSHARE);
if (ret)
exit(EXIT_FAILURE);

for (i = 51; i <= 92; i++)
if (fcntl(open_fds[i], F_GETFL) != -1)
exit(EXIT_FAILURE);

for (i = 93; i <= 100; i++)
if (fcntl(open_fds[i], F_GETFL) == -1)
exit(EXIT_FAILURE);

/* test that the kernel caps and still closes all fds */
ret = sys_close_range(open_fds[93], open_fds[99],
CLOSE_RANGE_UNSHARE);
if (ret)
exit(EXIT_FAILURE);

for (i = 93; i <= 99; i++)
if (fcntl(open_fds[i], F_GETFL) != -1)
exit(EXIT_FAILURE);

if (fcntl(open_fds[100], F_GETFL) == -1)
exit(EXIT_FAILURE);

ret = sys_close_range(open_fds[100], open_fds[100],
CLOSE_RANGE_UNSHARE);
if (ret)
exit(EXIT_FAILURE);

if (fcntl(open_fds[100], F_GETFL) != -1)
exit(EXIT_FAILURE);

exit(EXIT_SUCCESS);
}

EXPECT_EQ(waitpid(pid, &status, 0), pid);
EXPECT_EQ(true, WIFEXITED(status));
EXPECT_EQ(0, WEXITSTATUS(status));
}

TEST(close_range_unshare_capped)
{
int i, ret, status;
pid_t pid;
int open_fds[101];
struct clone_args args = {
.flags = CLONE_FILES,
.exit_signal = SIGCHLD,
};

for (i = 0; i < ARRAY_SIZE(open_fds); i++) {
int fd;

fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
ASSERT_GE(fd, 0) {
if (errno == ENOENT)
XFAIL(return, "Skipping test since /dev/null does not exist");
}

open_fds[i] = fd;
}

pid = sys_clone3(&args, sizeof(args));
ASSERT_GE(pid, 0);

if (pid == 0) {
ret = sys_close_range(open_fds[0], UINT_MAX,
CLOSE_RANGE_UNSHARE);
if (ret)
exit(EXIT_FAILURE);

for (i = 0; i <= 100; i++)
if (fcntl(open_fds[i], F_GETFL) != -1)
exit(EXIT_FAILURE);

exit(EXIT_SUCCESS);
}

EXPECT_EQ(waitpid(pid, &status, 0), pid);
EXPECT_EQ(true, WIFEXITED(status));
EXPECT_EQ(0, WEXITSTATUS(status));
}

TEST_HARNESS_MAIN