Skip to content

Commit 611fbeb

Browse files
Al Viroshuahkh
authored andcommitted
selftests:core: test coverage for dup_fd() failure handling in unshare_fd()
At some point there'd been a dumb braino during the dup_fd() calling conventions change; caught by smatch and immediately fixed. The trouble is, there had been no test coverage for the dup_fd() failure handling - neither in kselftests nor in LTP. Fortunately, it can be triggered on stock kernel - ENOMEM would require fault injection, but EMFILE can be had with sysctl alone (fs.nr_open). Add a test for dup_fd() failure. Fixed up commit log and short log - Shuah Khan Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent c049ace commit 611fbeb

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

tools/testing/selftests/core/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
CFLAGS += -g $(KHDR_INCLUDES)
33

4-
TEST_GEN_PROGS := close_range_test
4+
TEST_GEN_PROGS := close_range_test unshare_test
55

66
include ../lib.mk
77

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#define _GNU_SOURCE
4+
#include <errno.h>
5+
#include <fcntl.h>
6+
#include <linux/kernel.h>
7+
#include <limits.h>
8+
#include <stdbool.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <syscall.h>
13+
#include <unistd.h>
14+
#include <sys/resource.h>
15+
#include <linux/close_range.h>
16+
17+
#include "../kselftest_harness.h"
18+
#include "../clone3/clone3_selftests.h"
19+
20+
TEST(unshare_EMFILE)
21+
{
22+
pid_t pid;
23+
int status;
24+
struct __clone_args args = {
25+
.flags = CLONE_FILES,
26+
.exit_signal = SIGCHLD,
27+
};
28+
int fd;
29+
ssize_t n, n2;
30+
static char buf[512], buf2[512];
31+
struct rlimit rlimit;
32+
int nr_open;
33+
34+
fd = open("/proc/sys/fs/nr_open", O_RDWR);
35+
ASSERT_GE(fd, 0);
36+
37+
n = read(fd, buf, sizeof(buf));
38+
ASSERT_GT(n, 0);
39+
ASSERT_EQ(buf[n - 1], '\n');
40+
41+
ASSERT_EQ(sscanf(buf, "%d", &nr_open), 1);
42+
43+
ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit));
44+
45+
/* bump fs.nr_open */
46+
n2 = sprintf(buf2, "%d\n", nr_open + 1024);
47+
lseek(fd, 0, SEEK_SET);
48+
write(fd, buf2, n2);
49+
50+
/* bump ulimit -n */
51+
rlimit.rlim_cur = nr_open + 1024;
52+
rlimit.rlim_max = nr_open + 1024;
53+
EXPECT_EQ(0, setrlimit(RLIMIT_NOFILE, &rlimit)) {
54+
lseek(fd, 0, SEEK_SET);
55+
write(fd, buf, n);
56+
exit(EXIT_FAILURE);
57+
}
58+
59+
/* get a descriptor past the old fs.nr_open */
60+
EXPECT_GE(dup2(2, nr_open + 64), 0) {
61+
lseek(fd, 0, SEEK_SET);
62+
write(fd, buf, n);
63+
exit(EXIT_FAILURE);
64+
}
65+
66+
/* get descriptor table shared */
67+
pid = sys_clone3(&args, sizeof(args));
68+
EXPECT_GE(pid, 0) {
69+
lseek(fd, 0, SEEK_SET);
70+
write(fd, buf, n);
71+
exit(EXIT_FAILURE);
72+
}
73+
74+
if (pid == 0) {
75+
int err;
76+
77+
/* restore fs.nr_open */
78+
lseek(fd, 0, SEEK_SET);
79+
write(fd, buf, n);
80+
/* ... and now unshare(CLONE_FILES) must fail with EMFILE */
81+
err = unshare(CLONE_FILES);
82+
EXPECT_EQ(err, -1)
83+
exit(EXIT_FAILURE);
84+
EXPECT_EQ(errno, EMFILE)
85+
exit(EXIT_FAILURE);
86+
exit(EXIT_SUCCESS);
87+
}
88+
89+
EXPECT_EQ(waitpid(pid, &status, 0), pid);
90+
EXPECT_EQ(true, WIFEXITED(status));
91+
EXPECT_EQ(0, WEXITSTATUS(status));
92+
}
93+
94+
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)